--- a/Cargo.lock
+++ b/Cargo.lock
@@ -133,16 +133,24 @@ dependencies = [
"log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "base64"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1909,16 +1917,26 @@ version = "1.0.66"
source = "git+https://github.com/servo/serde?branch=deserialize_from_enums8#c4457d804b38b14e699b45c01d1909f93f25ab5e"
dependencies = [
"proc-macro2 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
+name = "serde_json"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "servo_arc"
version = "0.1.1"
dependencies = [
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -2391,21 +2409,25 @@ dependencies = [
"same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "webdriver"
version = "0.36.0"
dependencies = [
+ "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
+ "serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)",
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "webidl"
version = "0.6.0"
@@ -2609,16 +2631,17 @@ dependencies = [
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774"
"checksum arrayref 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd1479b7c29641adbd35ff3b5c293922d696a92f25c8c975da3e0acbc87258f"
"checksum arrayvec 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2f0ef4a9820019a0c91d918918c93dc71d469f581a49b47ddc1d285d4270bbe2"
"checksum ascii-canvas 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b385d69402821a1c254533a011a312531cbcc0e3e24f19bbb4747a5a2daf37e2"
"checksum atomic_refcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb2dcb6e6d35f20276943cc04bb98e538b348d525a04ac79c10021561d202f21"
"checksum atty 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d0fd4c0631f06448cc45a6bbb3b710ebb7ff8ccb96a0800c994afe23a70d5df2"
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
+"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
"checksum base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "96434f987501f0ed4eb336a411e0631ecd1afa11574fe148587adc4ff96143c9"
"checksum binary-space-partition 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "88ceb0d16c4fd0e42876e298d7d3ce3780dd9ebdcbe4199816a32c77e08597ff"
"checksum bincode 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bda13183df33055cbb84b847becce220d392df502ebe7a4a78d7021771ed94d0"
"checksum bindgen 0.37.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1b25ab82877ea8fe6ce1ce1f8ac54361f0218bad900af9eb11803994bf67c221"
"checksum binjs_meta 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fd7ca5635f1c6f94aaef7de76cb834c5920578355ce41dbcaf731b7ebe348518"
"checksum bit-set 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9bf6104718e80d7b26a68fdbacff3481cfc05df670821affc7e9cbc1884400c"
"checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f"
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
@@ -2783,16 +2806,17 @@ dependencies = [
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918"
"checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95"
"checksum serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3"
"checksum serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)" = "<none>"
+"checksum serde_json 1.0.22 (registry+https://github.com/rust-lang/crates.io-index)" = "84b8035cabe9b35878adec8ac5fe03d5f6bc97ff6edd7ccb96b44c1276ba390e"
"checksum simd 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3686dd9418ebcc3a26a0c0ae56deab0681e53fe899af91f5bbcee667ebffb1"
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
"checksum smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5c63726029f0069f88467873e47f392575f28f9f16b72ac65465263db4b3a13c"
"checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8"
"checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b"
"checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423"
"checksum string_cache_codegen 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "479cde50c3539481f33906a387f2bd17c8e87cb848c35b6021d41fb81ff9b4d7"
--- a/testing/webdriver/Cargo.toml
+++ b/testing/webdriver/Cargo.toml
@@ -6,15 +6,19 @@ description = "Library implementing the
keywords = ["webdriver", "browser", "automation", "protocol", "w3c"]
documentation = "https://docs.rs/webdriver"
repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/webdriver"
readme = "README.md"
license = "MPL-2.0"
[dependencies]
cookie = { version = "0.10", default-features = false }
+base64 = "0.6"
hyper = "0.10"
+lazy_static = "1.0"
log = "0.4"
regex = "1.0"
-rustc-serialize = "0.3"
+serde = "1.0"
+serde_json = "1.0"
+serde_derive = "1.0"
time = "0.1"
unicode-segmentation = "1.2"
url = "1"
--- a/testing/webdriver/src/actions.rs
+++ b/testing/webdriver/src/actions.rs
@@ -1,660 +1,1055 @@
-use command::Parameters;
-use common::{Nullable, WebElement};
-use error::{WebDriverResult, WebDriverError, ErrorStatus};
-use rustc_serialize::json::{ToJson, Json};
+use common::WebElement;
+use serde::de::{self, Deserialize, Deserializer};
+use serde::ser::{Serialize, Serializer};
+use std::default::Default;
use unicode_segmentation::UnicodeSegmentation;
-use std::collections::BTreeMap;
-use std::default::Default;
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct ActionSequence {
- pub id: Nullable<String>,
- pub actions: ActionsType
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub id: Option<String>,
+ #[serde(flatten)]
+ pub actions: ActionsType,
}
-impl Parameters for ActionSequence {
- fn from_json(body: &Json) -> WebDriverResult<ActionSequence> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Actions chain was not an object");
-
- let type_name = try_opt!(try_opt!(data.get("type"),
- ErrorStatus::InvalidArgument,
- "Missing type parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter ;type' was not a string");
-
- let id = match data.get("id") {
- Some(x) => Some(try_opt!(x.as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'id' was not a string").to_owned()),
- None => None
- };
-
-
- // Note that unlike the spec we get the pointer parameters in ActionsType::from_json
-
- let actions = match type_name {
- "none" | "key" | "pointer" => try!(ActionsType::from_json(&body)),
- _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Invalid action type"))
- };
-
- Ok(ActionSequence {
- id: id.into(),
- actions: actions
- })
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum ActionsType {
+ #[serde(rename = "none")]
+ Null { actions: Vec<NullActionItem> },
+ #[serde(rename = "key")]
+ Key { actions: Vec<KeyActionItem> },
+ #[serde(rename = "pointer")]
+ Pointer {
+ parameters: PointerActionParameters,
+ actions: Vec<PointerActionItem>,
}
}
-impl ToJson for ActionSequence {
- fn to_json(&self) -> Json {
- let mut data: BTreeMap<String, Json> = BTreeMap::new();
- data.insert("id".into(), self.id.to_json());
- let (action_type, actions) = match self.actions {
- ActionsType::Null(ref actions) => {
- ("none",
- actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
- }
- ActionsType::Key(ref actions) => {
- ("key",
- actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
- }
- ActionsType::Pointer(ref parameters, ref actions) => {
- data.insert("parameters".into(), parameters.to_json());
- ("pointer",
- actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>())
- }
- };
- data.insert("type".into(), action_type.to_json());
- data.insert("actions".into(), actions.to_json());
- Json::Object(data)
- }
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum NullActionItem {
+ General(GeneralAction),
}
-#[derive(Debug, PartialEq)]
-pub enum ActionsType {
- Null(Vec<NullActionItem>),
- Key(Vec<KeyActionItem>),
- Pointer(PointerActionParameters, Vec<PointerActionItem>)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum GeneralAction {
+ #[serde(rename="pause")]
+ Pause(PauseAction),
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct PauseAction {
+ pub duration: u64,
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum KeyActionItem {
+ General(GeneralAction),
+ Key(KeyAction),
}
-impl Parameters for ActionsType {
- fn from_json(body: &Json) -> WebDriverResult<ActionsType> {
- // These unwraps are OK as long as this is only called from ActionSequence::from_json
- let data = body.as_object().expect("Body should be a JSON Object");
- let actions_type = body.find("type").and_then(|x| x.as_string()).expect("Type should be a string");
- let actions_chain = try_opt!(try_opt!(data.get("actions"),
- ErrorStatus::InvalidArgument,
- "Missing actions parameter").as_array(),
- ErrorStatus::InvalidArgument,
- "Parameter 'actions' was not an array");
- match actions_type {
- "none" => {
- let mut actions = Vec::with_capacity(actions_chain.len());
- for action_body in actions_chain.iter() {
- actions.push(try!(NullActionItem::from_json(action_body)));
- };
- Ok(ActionsType::Null(actions))
- },
- "key" => {
- let mut actions = Vec::with_capacity(actions_chain.len());
- for action_body in actions_chain.iter() {
- actions.push(try!(KeyActionItem::from_json(action_body)));
- };
- Ok(ActionsType::Key(actions))
- },
- "pointer" => {
- let mut actions = Vec::with_capacity(actions_chain.len());
- let parameters = match data.get("parameters") {
- Some(x) => try!(PointerActionParameters::from_json(x)),
- None => Default::default()
- };
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum KeyAction {
+ #[serde(rename="keyDown")]
+ Down(KeyDownAction),
+ #[serde(rename="keyUp")]
+ Up(KeyUpAction),
+}
- for action_body in actions_chain.iter() {
- actions.push(try!(PointerActionItem::from_json(action_body)));
- }
- Ok(ActionsType::Pointer(parameters, actions))
- }
- _ => panic!("Got unexpected action type after checking type")
- }
- }
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct KeyDownAction {
+ #[serde(deserialize_with = "deserialize_key_action_value")]
+ pub value: String,
}
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct KeyUpAction {
+ #[serde(deserialize_with = "deserialize_key_action_value")]
+ pub value: String,
+}
+
+fn deserialize_key_action_value<'de, D>(deserializer: D) -> Result<String, D::Error>
+ where D: Deserializer<'de>
+{
+ String::deserialize(deserializer).map(|value| {
+ // Only a single Unicode grapheme cluster is allowed
+ if value.graphemes(true).collect::<Vec<&str>>().len() != 1 {
+ return Err(de::Error::custom(
+ format!("'{}' should only contain a single Unicode code point", value)));
+ }
+
+ Ok(value)
+ })?
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all="lowercase")]
pub enum PointerType {
Mouse,
Pen,
Touch,
}
-impl Parameters for PointerType {
- fn from_json(body: &Json) -> WebDriverResult<PointerType> {
- match body.as_string() {
- Some("mouse") => Ok(PointerType::Mouse),
- Some("pen") => Ok(PointerType::Pen),
- Some("touch") => Ok(PointerType::Touch),
- Some(_) => Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Unsupported pointer type"
- )),
- None => Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Pointer type was not a string"
- ))
- }
- }
-}
-
-impl ToJson for PointerType {
- fn to_json(&self) -> Json {
- match *self {
- PointerType::Mouse => "mouse".to_json(),
- PointerType::Pen => "pen".to_json(),
- PointerType::Touch => "touch".to_json(),
- }.to_json()
- }
-}
-
impl Default for PointerType {
fn default() -> PointerType {
PointerType::Mouse
}
}
-#[derive(Debug, Default, PartialEq)]
+#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PointerActionParameters {
- pub pointer_type: PointerType
-}
-
-impl Parameters for PointerActionParameters {
- fn from_json(body: &Json) -> WebDriverResult<PointerActionParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Parameter 'parameters' was not an object");
- let pointer_type = match data.get("pointerType") {
- Some(x) => try!(PointerType::from_json(x)),
- None => PointerType::default()
- };
- Ok(PointerActionParameters {
- pointer_type: pointer_type
- })
- }
-}
-
-impl ToJson for PointerActionParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("pointerType".to_owned(),
- self.pointer_type.to_json());
- Json::Object(data)
- }
+ #[serde(rename = "pointerType")]
+ pub pointer_type: PointerType,
}
-#[derive(Debug, PartialEq)]
-pub enum NullActionItem {
- General(GeneralAction)
-}
-
-impl Parameters for NullActionItem {
- fn from_json(body: &Json) -> WebDriverResult<NullActionItem> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Actions chain was not an object");
- let type_name = try_opt!(
- try_opt!(data.get("type"),
- ErrorStatus::InvalidArgument,
- "Missing 'type' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'type' was not a string");
- match type_name {
- "pause" => Ok(NullActionItem::General(
- try!(GeneralAction::from_json(body)))),
- _ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Invalid type attribute"))
- }
- }
-}
-
-impl ToJson for NullActionItem {
- fn to_json(&self) -> Json {
- match self {
- &NullActionItem::General(ref x) => x.to_json(),
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum KeyActionItem {
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum PointerActionItem {
General(GeneralAction),
- Key(KeyAction)
+ Pointer(PointerAction),
}
-impl Parameters for KeyActionItem {
- fn from_json(body: &Json) -> WebDriverResult<KeyActionItem> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Key action item was not an object");
- let type_name = try_opt!(
- try_opt!(data.get("type"),
- ErrorStatus::InvalidArgument,
- "Missing 'type' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'type' was not a string");
- match type_name {
- "pause" => Ok(KeyActionItem::General(
- try!(GeneralAction::from_json(body)))),
- _ => Ok(KeyActionItem::Key(
- try!(KeyAction::from_json(body))))
- }
- }
-}
-
-impl ToJson for KeyActionItem {
- fn to_json(&self) -> Json {
- match *self {
- KeyActionItem::General(ref x) => x.to_json(),
- KeyActionItem::Key(ref x) => x.to_json()
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum PointerActionItem {
- General(GeneralAction),
- Pointer(PointerAction)
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+#[serde(tag = "type")]
+pub enum PointerAction {
+ #[serde(rename="pointerCancel")]
+ Cancel,
+ #[serde(rename="pointerDown")]
+ Down(PointerDownAction),
+ #[serde(rename="pointerMove")]
+ Move(PointerMoveAction),
+ #[serde(rename="pointerUp")]
+ Up(PointerUpAction),
}
-impl Parameters for PointerActionItem {
- fn from_json(body: &Json) -> WebDriverResult<PointerActionItem> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Pointer action item was not an object");
- let type_name = try_opt!(
- try_opt!(data.get("type"),
- ErrorStatus::InvalidArgument,
- "Missing 'type' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'type' was not a string");
-
- match type_name {
- "pause" => Ok(PointerActionItem::General(try!(GeneralAction::from_json(body)))),
- _ => Ok(PointerActionItem::Pointer(try!(PointerAction::from_json(body))))
- }
- }
-}
-
-impl ToJson for PointerActionItem {
- fn to_json(&self) -> Json {
- match self {
- &PointerActionItem::General(ref x) => x.to_json(),
- &PointerActionItem::Pointer(ref x) => x.to_json()
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum GeneralAction {
- Pause(PauseAction)
-}
-
-impl Parameters for GeneralAction {
- fn from_json(body: &Json) -> WebDriverResult<GeneralAction> {
- match body.find("type").and_then(|x| x.as_string()) {
- Some("pause") => Ok(GeneralAction::Pause(try!(PauseAction::from_json(body)))),
- _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Invalid or missing type attribute"))
- }
- }
-}
-
-impl ToJson for GeneralAction {
- fn to_json(&self) -> Json {
- match self {
- &GeneralAction::Pause(ref x) => x.to_json()
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct PauseAction {
- pub duration: u64
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct PointerDownAction {
+ pub button: u64,
}
-impl Parameters for PauseAction {
- fn from_json(body: &Json) -> WebDriverResult<PauseAction> {
- let default = Json::U64(0);
- Ok(PauseAction {
- duration: try_opt!(body.find("duration").unwrap_or(&default).as_u64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'duration' was not a positive integer")
- })
- }
-}
-
-impl ToJson for PauseAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "pause".to_json());
- data.insert("duration".to_owned(),
- self.duration.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum KeyAction {
- Up(KeyUpAction),
- Down(KeyDownAction)
-}
-
-impl Parameters for KeyAction {
- fn from_json(body: &Json) -> WebDriverResult<KeyAction> {
- match body.find("type").and_then(|x| x.as_string()) {
- Some("keyDown") => Ok(KeyAction::Down(try!(KeyDownAction::from_json(body)))),
- Some("keyUp") => Ok(KeyAction::Up(try!(KeyUpAction::from_json(body)))),
- Some(_) | None => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Invalid type attribute value for key action"))
- }
- }
-}
-
-impl ToJson for KeyAction {
- fn to_json(&self) -> Json {
- match self {
- &KeyAction::Down(ref x) => x.to_json(),
- &KeyAction::Up(ref x) => x.to_json(),
- }
- }
-}
-
-fn validate_key_value(value_str: &str) -> WebDriverResult<String> {
- let mut graphemes = value_str.graphemes(true);
- let value = if let Some(g) = graphemes.next() {
- g
- } else {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Parameter 'value' was an empty string"))
- };
- if graphemes.next().is_some() {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Parameter 'value' contained multiple graphemes"))
- };
- Ok(value.to_string())
-}
-
-#[derive(Debug, PartialEq)]
-pub struct KeyUpAction {
- pub value: String
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct PointerMoveAction {
+ #[serde(
+ default,
+ skip_serializing_if = "Option::is_none",
+ deserialize_with = "deserialize_to_option_u64")]
+ pub duration: Option<u64>,
+ #[serde(default)]
+ pub origin: PointerOrigin,
+ #[serde(
+ default,
+ skip_serializing_if = "Option::is_none",
+ deserialize_with = "deserialize_to_option_i64")]
+ pub x: Option<i64>,
+ #[serde(
+ default,
+ skip_serializing_if = "Option::is_none",
+ deserialize_with = "deserialize_to_option_i64")]
+ pub y: Option<i64>,
}
-impl Parameters for KeyUpAction {
- fn from_json(body: &Json) -> WebDriverResult<KeyUpAction> {
- let value_str = try_opt!(
- try_opt!(body.find("value"),
- ErrorStatus::InvalidArgument,
- "Missing value parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'value' was not a string");
-
- let value = try!(validate_key_value(value_str));
- Ok(KeyUpAction {
- value: value
- })
- }
-}
-
-impl ToJson for KeyUpAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "keyUp".to_json());
- data.insert("value".to_string(),
- self.value.to_string().to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct KeyDownAction {
- pub value: String
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct PointerUpAction {
+ pub button: u64,
}
-impl Parameters for KeyDownAction {
- fn from_json(body: &Json) -> WebDriverResult<KeyDownAction> {
- let value_str = try_opt!(
- try_opt!(body.find("value"),
- ErrorStatus::InvalidArgument,
- "Missing value parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Parameter 'value' was not a string");
- let value = try!(validate_key_value(value_str));
- Ok(KeyDownAction {
- value: value
- })
- }
-}
-
-impl ToJson for KeyDownAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "keyDown".to_json());
- data.insert("value".to_owned(),
- self.value.to_string().to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum PointerOrigin {
- Viewport,
+ #[serde(
+ rename = "element-6066-11e4-a52e-4f735466cecf",
+ serialize_with = "serialize_webelement_id",
+ deserialize_with = "deserialize_webelement_id")]
+ Element(WebElement),
+ #[serde(rename = "pointer")]
Pointer,
- Element(WebElement),
-}
-
-impl Parameters for PointerOrigin {
- fn from_json(body: &Json) -> WebDriverResult<PointerOrigin> {
- match *body {
- Json::String(ref x) => {
- match &**x {
- "viewport" => Ok(PointerOrigin::Viewport),
- "pointer" => Ok(PointerOrigin::Pointer),
- _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Unknown pointer origin"))
- }
- },
- Json::Object(_) => Ok(PointerOrigin::Element(try!(WebElement::from_json(body)))),
- _ => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Pointer origin was not a string or an object"))
- }
- }
-}
-
-impl ToJson for PointerOrigin {
- fn to_json(&self) -> Json {
- match *self {
- PointerOrigin::Viewport => "viewport".to_json(),
- PointerOrigin::Pointer => "pointer".to_json(),
- PointerOrigin::Element(ref x) => x.to_json(),
- }
- }
+ #[serde(rename = "viewport")]
+ Viewport,
}
impl Default for PointerOrigin {
fn default() -> PointerOrigin {
PointerOrigin::Viewport
}
}
-#[derive(Debug, PartialEq)]
-pub enum PointerAction {
- Up(PointerUpAction),
- Down(PointerDownAction),
- Move(PointerMoveAction),
- Cancel
+fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
+ where S: Serializer
+{
+ element.id.serialize(serializer)
}
-impl Parameters for PointerAction {
- fn from_json(body: &Json) -> WebDriverResult<PointerAction> {
- match body.find("type").and_then(|x| x.as_string()) {
- Some("pointerUp") => Ok(PointerAction::Up(try!(PointerUpAction::from_json(body)))),
- Some("pointerDown") => Ok(PointerAction::Down(try!(PointerDownAction::from_json(body)))),
- Some("pointerMove") => Ok(PointerAction::Move(try!(PointerMoveAction::from_json(body)))),
- Some("pointerCancel") => Ok(PointerAction::Cancel),
- Some(_) | None => Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Missing or invalid type argument for pointer action"))
- }
- }
+fn deserialize_webelement_id<'de, D>(deserializer: D) -> Result<WebElement, D::Error>
+ where D: Deserializer<'de>
+{
+ String::deserialize(deserializer).map(|id| WebElement { id })
}
-impl ToJson for PointerAction {
- fn to_json(&self) -> Json {
- match self {
- &PointerAction::Down(ref x) => x.to_json(),
- &PointerAction::Up(ref x) => x.to_json(),
- &PointerAction::Move(ref x) => x.to_json(),
- &PointerAction::Cancel => {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "pointerCancel".to_json());
- Json::Object(data)
- }
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct PointerUpAction {
- pub button: u64,
+fn deserialize_to_option_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
+ where D: Deserializer<'de>
+{
+ Option::deserialize(deserializer)?
+ .ok_or_else(|| de::Error::custom("invalid type: null, expected i64"))
+ .map(|v: i64| Some(v))
}
-impl Parameters for PointerUpAction {
- fn from_json(body: &Json) -> WebDriverResult<PointerUpAction> {
- let button = try_opt!(
- try_opt!(body.find("button"),
- ErrorStatus::InvalidArgument,
- "Missing button parameter").as_u64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'button' was not a positive integer");
-
- Ok(PointerUpAction {
- button: button
- })
- }
-}
-
-impl ToJson for PointerUpAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "pointerUp".to_json());
- data.insert("button".to_owned(), self.button.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct PointerDownAction {
- pub button: u64,
+fn deserialize_to_option_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
+ where D: Deserializer<'de>
+{
+ Option::deserialize(deserializer)?
+ .ok_or_else(|| de::Error::custom("invalid type: null, expected i64"))
+ .map(|v: u64| Some(v))
}
-impl Parameters for PointerDownAction {
- fn from_json(body: &Json) -> WebDriverResult<PointerDownAction> {
- let button = try_opt!(
- try_opt!(body.find("button"),
- ErrorStatus::InvalidArgument,
- "Missing button parameter").as_u64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'button' was not a positive integer");
-
- Ok(PointerDownAction {
- button: button
- })
- }
-}
+#[cfg(test)]
+mod test {
+ use serde_json;
+ use super::*;
+ use test::{check_deserialize, check_serialize_deserialize};
-impl ToJson for PointerDownAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(),
- "pointerDown".to_json());
- data.insert("button".to_owned(), self.button.to_json());
- Json::Object(data)
- }
-}
+ #[test]
+ fn test_json_action_sequence_null() {
+ let json = r#"{
+ "id":"none",
+ "type":"none",
+ "actions":[{
+ "type":"pause","duration":1
+ }]
+ }"#;
+ let data = ActionSequence {
+ id: Some("none".into()),
+ actions: ActionsType::Null {
+ actions: vec!{
+ NullActionItem::General (
+ GeneralAction::Pause ( PauseAction { duration: 1 } )
+ )
+ }
+ }
+ };
-#[derive(Debug, PartialEq)]
-pub struct PointerMoveAction {
- pub duration: Nullable<u64>,
- pub origin: PointerOrigin,
- pub x: Nullable<i64>,
- pub y: Nullable<i64>
-}
+ check_serialize_deserialize(&json, &data);
+ }
-impl Parameters for PointerMoveAction {
- fn from_json(body: &Json) -> WebDriverResult<PointerMoveAction> {
- let duration = match body.find("duration") {
- Some(duration) => Some(try_opt!(duration.as_u64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'duration' was not a positive integer")),
- None => None
-
+ #[test]
+ fn test_json_action_sequence_key() {
+ let json = r#"{
+ "id":"some_key",
+ "type":"key",
+ "actions":[
+ {"type":"keyDown","value":"f"}
+ ]
+ }"#;
+ let data = ActionSequence {
+ id: Some("some_key".into()),
+ actions: ActionsType::Key {
+ actions: vec!{
+ KeyActionItem::Key (
+ KeyAction::Down ( KeyDownAction { value: String::from("f") } )
+ )
+ }
+ }
};
- let origin = match body.find("origin") {
- Some(o) => try!(PointerOrigin::from_json(o)),
- None => PointerOrigin::default()
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_action_sequence_pointer() {
+ let json = r#"{
+ "id":"some_pointer",
+ "type":"pointer",
+ "parameters":{
+ "pointerType":"mouse"
+ },
+ "actions":[
+ {"type":"pointerDown","button":0},
+ {"type":"pointerMove","origin":"pointer","x":10,"y":20},
+ {"type":"pointerUp","button":0}
+ ]
+ }"#;
+ let data = ActionSequence {
+ id: Some("some_pointer".into()),
+ actions: ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: PointerType::Mouse
+ },
+ actions: vec!{
+ PointerActionItem::Pointer (
+ PointerAction::Down ( PointerDownAction { button: 0 } )
+ ),
+ PointerActionItem::Pointer (
+ PointerAction::Move ( PointerMoveAction {
+ origin: PointerOrigin::Pointer,
+ duration: None,
+ x: Some(10),
+ y: Some(20),
+ })
+ ),
+ PointerActionItem::Pointer (
+ PointerAction::Up ( PointerUpAction { button: 0 } )
+ ),
+ }
+ }
+ };
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_action_sequence_actions_missing() {
+ let json = r#"{
+ "id": "3"
+ }"#;
+
+ assert!(serde_json::from_str::<ActionSequence>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_action_sequence_actions_null() {
+ let json = r#"{
+ "id": "3",
+ "actions": null
+ }"#;
+
+ assert!(serde_json::from_str::<ActionSequence>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_action_sequence_actions_invalid_type() {
+ let json = r#"{
+ "id": "3",
+ "actions": "foo"
+ }"#;
+
+ assert!(serde_json::from_str::<ActionSequence>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_actions_type_null() {
+ let json = r#"{
+ "type":"none",
+ "actions":[{
+ "type":"pause",
+ "duration":1
+ }]
+ }"#;
+ let data = ActionsType::Null {
+ actions: vec!{
+ NullActionItem::General (
+ GeneralAction::Pause ( PauseAction { duration: 1 } )
+ )
+ }
+ };
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_actions_type_key() {
+ let json = r#"{
+ "type":"key",
+ "actions":[{
+ "type":"keyDown",
+ "value":"f"
+ }]
+ }"#;
+ let data = ActionsType::Key {
+ actions: vec!{
+ KeyActionItem::Key (
+ KeyAction::Down ( KeyDownAction { value: String::from("f") } )
+ )
+ }
+ };
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_actions_type_pointer() {
+ let json = r#"{
+ "type":"pointer",
+ "parameters":{"pointerType":"mouse"},
+ "actions":[
+ {"type":"pointerDown","button":1}
+ ]}"#;
+ let data = ActionsType::Pointer {
+ parameters: PointerActionParameters {
+ pointer_type: PointerType::Mouse
+ },
+ actions: vec!{
+ PointerActionItem::Pointer (
+ PointerAction::Down ( PointerDownAction { button: 1 } ),
+ )
+ }
};
- let x = match body.find("x") {
- Some(x) => {
- Some(try_opt!(x.as_i64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'x' was not an integer"))
- },
- None => None
- };
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_actions_type_invalid() {
+ let json = r#"{"actions":[{"foo":"bar"}]}"#;
+ assert!(serde_json::from_str::<ActionsType>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_null_action_item_general() {
+ let json = r#"{"type":"pause","duration":1}"#;
+ let data = NullActionItem::General ( GeneralAction::Pause (
+ PauseAction { duration: 1 }
+ ));
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_null_action_item_invalid_type() {
+ let json = r#"{"type":"invalid"}"#;
+ assert!(serde_json::from_str::<NullActionItem>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_general_action_pause() {
+ let json = r#"{"type":"pause","duration":1}"#;
+ let data = GeneralAction::Pause ( PauseAction { duration: 1 } );
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_general_action_pause_with_duration_missing() {
+ let json = r#"{"type":"pause"}"#;
+
+ assert!(serde_json::from_str::<GeneralAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_general_action_pause_with_duration_null() {
+ let json = r#"{"type":"pause","duration":null}"#;
+
+ assert!(serde_json::from_str::<GeneralAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_general_action_pause_with_duration_invalid_type() {
+ let json = r#"{"type":"pause","duration":"foo"}"#;
+
+ assert!(serde_json::from_str::<GeneralAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_general_action_pause_with_duration_negative() {
+ let json = r#"{"type":"pause","duration":-30}"#;
+
+ assert!(serde_json::from_str::<GeneralAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_item_general() {
+ let json = r#"{"type":"pause","duration":1}"#;
+ let data = KeyActionItem::General ( GeneralAction::Pause (
+ PauseAction { duration: 1 }
+ ));
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_item_key() {
+ let json = r#"{"type":"keyDown","value":"f"}"#;
+ let data = KeyActionItem::Key ( KeyAction::Down (
+ KeyDownAction { value: String::from("f") }
+ ));
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_item_invalid_type() {
+ let json = r#"{"type":"invalid"}"#;
+ assert!(serde_json::from_str::<KeyActionItem>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_missing_subtype() {
+ let json = r#"{"value":"f"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_wrong_subtype() {
+ let json = r#"{"type":"pause","value":"f"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_down() {
+ let json = r#"{"type":"keyDown","value":"f"}"#;
+ let data = KeyAction::Down(KeyDownAction {
+ value: "f".to_owned(),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_value_unicode() {
+ let json = r#"{"type":"keyDown","value":"à"}"#;
+ let data = KeyAction::Down(KeyDownAction {
+ value: "à".to_owned(),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_value_unicode_encoded() {
+ let json = r#"{"type":"keyDown","value":"\u00E0"}"#;
+ let data = KeyAction::Down(KeyDownAction {
+ value: "à".to_owned(),
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_value_missing() {
+ let json = r#"{"type":"keyDown"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_value_null() {
+ let json = r#"{"type":"keyDown","value":null}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_value_invalid_type() {
+ let json = r#"{"type":"keyDown,"value":["f","o","o"]}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_down_with_multiple_code_points() {
+ let json = r#"{"type":"keyDown","value":"fo"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_up() {
+ let json = r#"{"type":"keyUp","value":"f"}"#;
+ let data = KeyAction::Up(KeyUpAction {
+ value: "f".to_owned(),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_value_unicode() {
+ let json = r#"{"type":"keyUp","value":"à"}"#;
+ let data = KeyAction::Up(KeyUpAction {
+ value: "à".to_owned(),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_value_unicode_encoded() {
+ let json = r#"{"type":"keyUp","value":"\u00E0"}"#;
+ let data = KeyAction::Up(KeyUpAction {
+ value: "à".to_owned(),
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_value_missing() {
+ let json = r#"{"type":"keyUp"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_value_null() {
+ let json = r#"{"type":"keyUp,"value":null}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_value_invalid_type() {
+ let json = r#"{"type":"keyUp,"value":["f","o","o"]}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_key_action_up_with_multiple_code_points() {
+ let json = r#"{"type":"keyUp","value":"fo"}"#;
+
+ assert!(serde_json::from_str::<KeyAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_item_general() {
+ let json = r#"{"type":"pause","duration":1}"#;
+ let data = PointerActionItem::General ( GeneralAction::Pause (
+ PauseAction { duration: 1 }
+ ));
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_item_pointer() {
+ let json = r#"{"type":"pointerCancel"}"#;
+ let data = PointerActionItem::Pointer ( PointerAction::Cancel );
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_item_invalid() {
+ let json = r#"{"type":"invalid"}"#;
+
+ assert!(serde_json::from_str::<PointerActionItem>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_parameters_mouse() {
+ let json = r#"{"pointerType":"mouse"}"#;
+ let data = PointerActionParameters { pointer_type: PointerType::Mouse};
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_parameters_pen() {
+ let json = r#"{"pointerType":"pen"}"#;
+ let data = PointerActionParameters { pointer_type: PointerType::Pen};
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_parameters_touch() {
+ let json = r#"{"pointerType":"touch"}"#;
+ let data = PointerActionParameters { pointer_type: PointerType::Touch};
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_item_invalid_type() {
+ let json = r#"{"type":"pointerInvalid"}"#;
+ assert!(serde_json::from_str::<PointerActionItem>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_with_subtype_missing() {
+ let json = r#"{"button":1}"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_with_subtype_invalid() {
+ let json = r#"{"type":"invalid"}"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_with_subtype_wrong() {
+ let json = r#"{"type":"pointerMove",button":1}"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_cancel() {
+ let json = r#"{"type":"pointerCancel"}"#;
+ let data = PointerAction::Cancel;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_down() {
+ let json = r#"{"type":"pointerDown","button":1}"#;
+ let data = PointerAction::Down(PointerDownAction {
+ button: 1,
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_down_with_button_missing() {
+ let json = r#"{"type":"pointerDown"}"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_down_with_button_null() {
+ let json = r#"{
+ "type":"pointerDown",
+ "button":null
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
- let y = match body.find("y") {
- Some(y) => {
- Some(try_opt!(y.as_i64(),
- ErrorStatus::InvalidArgument,
- "Parameter 'y' was not an integer"))
- },
- None => None
- };
+ #[test]
+ fn test_json_pointer_action_down_with_button_invalid_type() {
+ let json = r#"{
+ "type":"pointerDown",
+ "button":"foo",
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_down_with_button_negative() {
+ let json = r#"{
+ "type":"pointerDown",
+ "button":-30
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+ let data = PointerAction::Move(PointerMoveAction {
+ duration: Some(100),
+ origin: PointerOrigin::Viewport,
+ x: Some(5),
+ y: Some(10),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_missing_subtype() {
+ let json = r#"{
+ "duration":100,
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_wrong_subtype() {
+ let json = r#"{
+ "type":"pointerUp",
+ "duration":100,
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_duration_missing() {
+ let json = r#"{
+ "type":"pointerMove",
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+ let data = PointerAction::Move(PointerMoveAction {
+ duration: None,
+ origin: PointerOrigin::Viewport,
+ x: Some(5),
+ y: Some(10),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_duration_null() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":null,
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_duration_invalid_type() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":"invalid",
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_duration_negative() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":-30,
+ "origin":"viewport",
+ "x":5,
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_origin_missing() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "x":5,
+ "y":10
+ }"#;
+ let data = PointerAction::Move(PointerMoveAction {
+ duration: Some(100),
+ origin: PointerOrigin::Viewport,
+ x: Some(5),
+ y: Some(10),
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_x_missing() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "y":10
+ }"#;
+ let data = PointerAction::Move(PointerMoveAction {
+ duration: Some(100),
+ origin: PointerOrigin::Viewport,
+ x: None,
+ y: Some(10),
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_x_null() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x": null,
+ "y":10
+ }"#;
- Ok(PointerMoveAction {
- duration: duration.into(),
- origin: origin.into(),
- x: x.into(),
- y: y.into(),
- })
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_x_invalid_type() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x": "invalid",
+ "y":10
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_y_missing() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x":5
+ }"#;
+ let data = PointerAction::Move(PointerMoveAction {
+ duration: Some(100),
+ origin: PointerOrigin::Viewport,
+ x: Some(5),
+ y: None,
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_y_null() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x":5,
+ "y":null
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_move_with_y_invalid_type() {
+ let json = r#"{
+ "type":"pointerMove",
+ "duration":100,
+ "origin":"viewport",
+ "x":5,
+ "y":"invalid"
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_up() {
+ let json = r#"{
+ "type":"pointerUp",
+ "button":1
+ }"#;
+ let data = PointerAction::Up(PointerUpAction {
+ button: 1,
+ });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_action_up_with_button_missing() {
+ let json = r#"{
+ "type":"pointerUp"
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_up_with_button_null() {
+ let json = r#"{
+ "type":"pointerUp",
+ "button":null
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_up_with_button_invalid_type() {
+ let json = r#"{
+ "type":"pointerUp",
+ "button":"foo",
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_action_up_with_button_negative() {
+ let json = r#"{
+ "type":"pointerUp",
+ "button":-30
+ }"#;
+
+ assert!(serde_json::from_str::<PointerAction>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_origin_pointer() {
+ let json = r#""pointer""#;
+ let data = PointerOrigin::Pointer;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_origin_viewport() {
+ let json = r#""viewport""#;
+ let data = PointerOrigin::Viewport;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_origin_web_element() {
+ let json = r#"{"element-6066-11e4-a52e-4f735466cecf":"elem"}"#;
+ let data = PointerOrigin::Element(WebElement { id: "elem".into() });
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_origin_invalid_type() {
+ let data = r#""invalid""#;
+ assert!(serde_json::from_str::<PointerOrigin>(&data).is_err());
+ }
+
+ #[test]
+ fn test_json_pointer_type_mouse() {
+ let json = r#""mouse""#;
+ let data = PointerType::Mouse;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_type_pen() {
+ let json = r#""pen""#;
+ let data = PointerType::Pen;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_type_touch() {
+ let json = r#""touch""#;
+ let data = PointerType::Touch;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_pointer_type_invalid_type() {
+ let json = r#""invalid""#;
+ assert!(serde_json::from_str::<PointerType>(&json).is_err());
}
}
-
-impl ToJson for PointerMoveAction {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("type".to_owned(), "pointerMove".to_json());
- if self.duration.is_value() {
- data.insert("duration".to_owned(),
- self.duration.to_json());
- }
-
- data.insert("origin".to_owned(), self.origin.to_json());
-
- if self.x.is_value() {
- data.insert("x".to_owned(), self.x.to_json());
- }
- if self.y.is_value() {
- data.insert("y".to_owned(), self.y.to_json());
- }
- Json::Object(data)
- }
-}
--- a/testing/webdriver/src/capabilities.rs
+++ b/testing/webdriver/src/capabilities.rs
@@ -1,15 +1,14 @@
-use command::Parameters;
-use error::{ErrorStatus, WebDriverError, WebDriverResult};
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
+use error::{WebDriverResult, WebDriverError, ErrorStatus};
+use std::convert::From;
+use serde_json::{Value, Map};
use url::Url;
-pub type Capabilities = BTreeMap<String, Json>;
+pub type Capabilities = Map<String, Value>;
/// Trait for objects that can be used to inspect browser capabilities
///
/// The main methods in this trait are called with a Capabilites object
/// resulting from a full set of potential capabilites for the session. Given
/// those Capabilities they return a property of the browser instance that
/// would be initiated. In many cases this will be independent of the input,
/// but in the case of e.g. browser version, it might depend on a path to the
@@ -41,35 +40,35 @@ pub trait BrowserCapabilities {
fn accept_insecure_certs(&mut self, &Capabilities) -> WebDriverResult<bool>;
/// Indicates whether driver supports all of the window resizing and
/// repositioning commands.
fn set_window_rect(&mut self, &Capabilities) -> WebDriverResult<bool>;
fn accept_proxy(
&mut self,
- proxy_settings: &BTreeMap<String, Json>,
+ proxy_settings: &Map<String, Value>,
&Capabilities,
) -> WebDriverResult<bool>;
/// Type check custom properties
///
/// Check that custom properties containing ":" have the correct data types.
/// Properties that are unrecognised must be ignored i.e. return without
/// error.
- fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()>;
+ fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()>;
/// Check if custom properties are accepted capabilites
///
/// Check that custom properties containing ":" are compatible with
/// the implementation.
fn accept_custom(
&mut self,
name: &str,
- value: &Json,
+ value: &Value,
merged: &Capabilities,
) -> WebDriverResult<bool>;
}
/// Trait to abstract over various version of the new session parameters
///
/// This trait is expected to be implemented on objects holding the capabilities
/// from a new session command.
@@ -78,32 +77,47 @@ pub trait CapabilitiesMatching {
///
/// Takes a BrowserCapabilites object and returns a set of capabilites that
/// are valid for that browser, if any, or None if there are no matching
/// capabilities.
fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
-> WebDriverResult<Option<Capabilities>>;
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct SpecNewSessionParameters {
+ #[serde(default = "Capabilities::default")]
pub alwaysMatch: Capabilities,
+ #[serde(default = "firstMatch_default")]
pub firstMatch: Vec<Capabilities>,
}
+impl Default for SpecNewSessionParameters {
+ fn default() -> Self {
+ SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() },
+ }
+ }
+}
+
+fn firstMatch_default() -> Vec<Capabilities> {
+ vec!{Capabilities::default()}
+}
+
impl SpecNewSessionParameters {
fn validate<T: BrowserCapabilities>(
&self,
mut capabilities: Capabilities,
browser_capabilities: &T,
) -> WebDriverResult<Capabilities> {
// Filter out entries with the value `null`
let null_entries = capabilities
.iter()
- .filter(|&(_, ref value)| **value == Json::Null)
+ .filter(|&(_, ref value)| **value == Value::Null)
.map(|(k, _)| k.clone())
.collect::<Vec<String>>();
for key in null_entries {
capabilities.remove(&key);
}
for (key, value) in capabilities.iter() {
match &**key {
@@ -140,19 +154,19 @@ impl SpecNewSessionParameters {
browser_capabilities.validate_custom(x, value)?
}
}
}
}
Ok(capabilities)
}
- fn validate_page_load_strategy(value: &Json) -> WebDriverResult<()> {
+ fn validate_page_load_strategy(value: &Value) -> WebDriverResult<()> {
match value {
- &Json::String(ref x) => {
+ &Value::String(ref x) => {
match &**x {
"normal" |
"eager" |
"none" => {},
x => {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("Invalid page load strategy: {}", x)))
@@ -160,38 +174,38 @@ impl SpecNewSessionParameters {
}
}
_ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
"pageLoadStrategy is not a string"))
}
Ok(())
}
- fn validate_proxy(proxy_value: &Json) -> WebDriverResult<()> {
+ fn validate_proxy(proxy_value: &Value) -> WebDriverResult<()> {
let obj = try_opt!(proxy_value.as_object(),
ErrorStatus::InvalidArgument,
"proxy is not an object");
for (key, value) in obj.iter() {
match &**key {
- "proxyType" => match value.as_string() {
+ "proxyType" => match value.as_str() {
Some("pac") |
Some("direct") |
Some("autodetect") |
Some("system") |
Some("manual") => {},
Some(x) => return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("Invalid proxyType value: {}", x))),
None => return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("proxyType is not a string: {}", value))),
},
- "proxyAutoconfigUrl" => match value.as_string() {
+ "proxyAutoconfigUrl" => match value.as_str() {
Some(x) => {
Url::parse(x).or(Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("proxyAutoconfigUrl is not a valid URL: {}", x))))?;
},
None => return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"proxyAutoconfigUrl is not a string"
@@ -214,21 +228,21 @@ impl SpecNewSessionParameters {
ErrorStatus::InvalidArgument,
format!("Invalid proxy configuration entry: {}", x)))
}
}
Ok(())
}
- fn validate_no_proxy(value: &Json) -> WebDriverResult<()> {
+ fn validate_no_proxy(value: &Value) -> WebDriverResult<()> {
match value.as_array() {
Some(hosts) => {
for host in hosts {
- match host.as_string() {
+ match host.as_str() {
Some(_) => {},
None => return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("noProxy item is not a string: {}", host)
))
}
}
},
@@ -238,18 +252,18 @@ impl SpecNewSessionParameters {
))
}
Ok(())
}
/// Validate whether a named capability is JSON value is a string containing a host
/// and possible port
- fn validate_host(value: &Json, entry: &str) -> WebDriverResult<()> {
- match value.as_string() {
+ fn validate_host(value: &Value, entry: &str) -> WebDriverResult<()> {
+ match value.as_str() {
Some(host) => {
if host.contains("://") {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
format!("{} must not contain a scheme: {}", entry, host)));
}
// Temporarily add a scheme so the host can be parsed as URL
@@ -273,17 +287,17 @@ impl SpecNewSessionParameters {
ErrorStatus::InvalidArgument,
format!("{} is not a string: {}", entry, value)
))
}
Ok(())
}
- fn validate_timeouts(value: &Json) -> WebDriverResult<()> {
+ fn validate_timeouts(value: &Value) -> WebDriverResult<()> {
let obj = try_opt!(
value.as_object(),
ErrorStatus::InvalidArgument,
"timeouts capability is not an object"
);
for (key, value) in obj.iter() {
match &**key {
@@ -308,19 +322,19 @@ impl SpecNewSessionParameters {
))
}
}
}
Ok(())
}
- fn validate_unhandled_prompt_behaviour(value: &Json) -> WebDriverResult<()> {
+ fn validate_unhandled_prompt_behaviour(value: &Value) -> WebDriverResult<()> {
let behaviour = try_opt!(
- value.as_string(),
+ value.as_str(),
ErrorStatus::InvalidArgument,
format!("unhandledPromptBehavior is not a string: {}", value)
);
match behaviour {
"accept" |
"accept and notify" |
"dismiss" |
@@ -333,101 +347,41 @@ impl SpecNewSessionParameters {
))
}
}
Ok(())
}
}
-impl Parameters for SpecNewSessionParameters {
- fn from_json(body: &Json) -> WebDriverResult<SpecNewSessionParameters> {
- let data = try_opt!(
- body.as_object(),
- ErrorStatus::UnknownError,
- format!("Malformed capabilities, message body is not an object: {}", body)
- );
-
- let capabilities = try_opt!(
- try_opt!(
- data.get("capabilities"),
- ErrorStatus::InvalidArgument,
- "Malformed capabilities, missing \"capabilities\" field"
- ).as_object(),
- ErrorStatus::InvalidArgument,
- "Malformed capabilities, \"capabilities\" field is not an object}"
- );
-
- let default_always_match = Json::Object(Capabilities::new());
- let always_match = try_opt!(
- capabilities
- .get("alwaysMatch")
- .unwrap_or(&default_always_match)
- .as_object(),
- ErrorStatus::InvalidArgument,
- "Malformed capabilities, alwaysMatch field is not an object"
- );
- let default_first_matches = Json::Array(vec![]);
- let first_matches = try_opt!(
- capabilities
- .get("firstMatch")
- .unwrap_or(&default_first_matches)
- .as_array(),
- ErrorStatus::InvalidArgument,
- "Malformed capabilities, firstMatch field is not an array"
- ).iter()
- .map(|x| {
- x.as_object().map(|x| x.clone()).ok_or(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "Malformed capabilities, firstMatch entry is not an object",
- ))
- })
- .collect::<WebDriverResult<Vec<Capabilities>>>()?;
-
- return Ok(SpecNewSessionParameters {
- alwaysMatch: always_match.clone(),
- firstMatch: first_matches,
- });
- }
-}
-
-impl ToJson for SpecNewSessionParameters {
- fn to_json(&self) -> Json {
- let mut body = BTreeMap::new();
- let mut capabilities = BTreeMap::new();
- capabilities.insert("alwaysMatch".into(), self.alwaysMatch.to_json());
- capabilities.insert("firstMatch".into(), self.firstMatch.to_json());
- body.insert("capabilities".into(), capabilities.to_json());
- Json::Object(body)
- }
-}
-
impl CapabilitiesMatching for SpecNewSessionParameters {
fn match_browser<T: BrowserCapabilities>(
&self,
browser_capabilities: &mut T,
) -> WebDriverResult<Option<Capabilities>> {
- let default = vec![BTreeMap::new()];
+ let default = vec![Map::new()];
let capabilities_list = if self.firstMatch.len() > 0 {
&self.firstMatch
} else {
&default
};
let merged_capabilities = capabilities_list
.iter()
.map(|first_match_entry| {
if first_match_entry.keys().any(|k| self.alwaysMatch.contains_key(k)) {
return Err(WebDriverError::new(
ErrorStatus::InvalidArgument,
"firstMatch key shadowed a value in alwaysMatch",
));
}
let mut merged = self.alwaysMatch.clone();
- merged.append(&mut first_match_entry.clone());
+ for (key, value) in first_match_entry.clone().into_iter() {
+ merged.insert(key, value);
+ }
Ok(merged)
})
.map(|merged| {
merged.and_then(|x| self.validate(x, browser_capabilities))
})
.collect::<WebDriverResult<Vec<Capabilities>>>()?;
let selected = merged_capabilities
@@ -438,67 +392,67 @@ impl CapabilitiesMatching for SpecNewSes
for (key, value) in merged.iter() {
match &**key {
"browserName" => {
let browserValue = browser_capabilities
.browser_name(merged)
.ok()
.and_then(|x| x);
- if value.as_string() != browserValue.as_ref().map(|x| &**x) {
+ if value.as_str() != browserValue.as_ref().map(|x| &**x) {
return None;
}
}
"browserVersion" => {
let browserValue = browser_capabilities
.browser_version(merged)
.ok()
.and_then(|x| x);
// We already validated this was a string
- let version_cond = value.as_string().unwrap_or("");
+ let version_cond = value.as_str().unwrap_or("");
if let Some(version) = browserValue {
if !browser_capabilities
.compare_browser_version(&*version, version_cond)
.unwrap_or(false)
{
return None;
}
} else {
return None;
}
}
"platformName" => {
let browserValue = browser_capabilities
.platform_name(merged)
.ok()
.and_then(|x| x);
- if value.as_string() != browserValue.as_ref().map(|x| &**x) {
+ if value.as_str() != browserValue.as_ref().map(|x| &**x) {
return None;
}
}
"acceptInsecureCerts" => {
- if value.as_boolean().unwrap_or(false) &&
+ if value.as_bool().unwrap_or(false) &&
!browser_capabilities
.accept_insecure_certs(merged)
.unwrap_or(false)
{
return None;
}
}
"setWindowRect" => {
- if value.as_boolean().unwrap_or(false) &&
+ if value.as_bool().unwrap_or(false) &&
!browser_capabilities
.set_window_rect(merged)
.unwrap_or(false)
{
return None;
}
}
"proxy" => {
- let default = BTreeMap::new();
+ let default = Map::new();
let proxy = value.as_object().unwrap_or(&default);
if !browser_capabilities
.accept_proxy(&proxy, merged)
.unwrap_or(false)
{
return None;
}
}
@@ -520,94 +474,192 @@ impl CapabilitiesMatching for SpecNewSes
return Some(merged);
})
.next()
.map(|x| x.clone());
Ok(selected)
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct LegacyNewSessionParameters {
+ #[serde(default = "Capabilities::default")]
pub desired: Capabilities,
+ #[serde(default = "Capabilities::default")]
pub required: Capabilities,
}
impl CapabilitiesMatching for LegacyNewSessionParameters {
fn match_browser<T: BrowserCapabilities>(
&self,
browser_capabilities: &mut T,
) -> WebDriverResult<Option<Capabilities>> {
// For now don't do anything much, just merge the
// desired and required and return the merged list.
- let mut capabilities: Capabilities = BTreeMap::new();
+ let mut capabilities: Capabilities = Map::new();
self.required.iter().chain(self.desired.iter()).fold(
&mut capabilities,
|caps, (key, value)| {
if !caps.contains_key(key) {
caps.insert(key.clone(), value.clone());
}
caps
},
);
browser_capabilities.init(&capabilities);
Ok(Some(capabilities))
}
}
-impl Parameters for LegacyNewSessionParameters {
- fn from_json(body: &Json) -> WebDriverResult<LegacyNewSessionParameters> {
- let data = try_opt!(
- body.as_object(),
- ErrorStatus::UnknownError,
- format!("Malformed legacy capabilities, message body is not an object: {}", body)
- );
+#[cfg(test)]
+mod tests {
+ use serde_json::{self, Value};
+ use super::*;
+ use test::check_deserialize;
+
+ fn validate_proxy(value: &str) -> WebDriverResult<()> {
+ let data = serde_json::from_str::<Value>(value).unwrap();
+ SpecNewSessionParameters::validate_proxy(&data)
+ }
+
+ #[test]
+ fn test_json_spec_new_session_parameters_alwaysMatch_only() {
+ let json = r#"{
+ "alwaysMatch":{}
+ }"#;
+ let data = SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() }
+ };
+
+ check_deserialize(&json, &data);
+ }
- let desired = if let Some(capabilities) = data.get("desiredCapabilities") {
- try_opt!(
- capabilities.as_object(),
- ErrorStatus::InvalidArgument,
- "Malformed legacy capabilities, desiredCapabilities field is not an object"
- ).clone()
- } else {
- BTreeMap::new()
+ #[test]
+ fn test_json_spec_new_session_parameters_firstMatch_only() {
+ let json = r#"{
+ "firstMatch":[{}]
+ }"#;
+ let data = SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() }
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_spec_new_session_parameters_alwaysMatch_null() {
+ let json = r#"{
+ "alwaysMatch":null,
+ "firstMatch":[{}]
+ }"#;
+
+ assert!(serde_json::from_str::<SpecNewSessionParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_spec_new_session_parameters_firstMatch_null() {
+ let json = r#"{
+ "alwaysMatch":{},
+ "firstMatch":null
+ }"#;
+
+ assert!(serde_json::from_str::<SpecNewSessionParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_spec_new_session_parameters_both_empty() {
+ let json = r#"{
+ "alwaysMatch":{},
+ "firstMatch":[{}]
+ }"#;
+ let data = SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() }
};
- let required = if let Some(capabilities) = data.get("requiredCapabilities") {
- try_opt!(
- capabilities.as_object(),
- ErrorStatus::InvalidArgument,
- "Malformed legacy capabilities, requiredCapabilities field is not an object"
- ).clone()
- } else {
- BTreeMap::new()
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_spec_new_session_parameters_both_with_capability() {
+ let json = r#"{
+ "alwaysMatch":{"foo":"bar"},
+ "firstMatch":[{"foo2":"bar2"}]
+ }"#;
+ let mut data = SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() }
+ };
+ data.alwaysMatch.insert("foo".into(), "bar".into());
+ data.firstMatch[0].insert("foo2".into(), "bar2".into());
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_desired_only() {
+ let json = r#"{"desired":{}}"#;
+ let data = LegacyNewSessionParameters {
+ desired: Capabilities::new(),
+ required: Capabilities::new(),
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_required_only() {
+ let json = r#"{"required":{}}"#;
+ let data = LegacyNewSessionParameters {
+ desired: Capabilities::new(),
+ required: Capabilities::new(),
};
- Ok(LegacyNewSessionParameters { desired, required })
+ check_deserialize(&json, &data);
}
-}
+
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_desired_null() {
+ let json = r#"{"desired":null,"required":{}}"#;
-impl ToJson for LegacyNewSessionParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("desiredCapabilities".to_owned(), self.desired.to_json());
- data.insert("requiredCapabilities".to_owned(), self.required.to_json());
- Json::Object(data)
+ assert!(serde_json::from_str::<LegacyNewSessionParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_required_null() {
+ let json = r#"{"desired":{}, "required":null}"#;
+
+ assert!(serde_json::from_str::<LegacyNewSessionParameters>(&json).is_err());
}
-}
+
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_both_empty() {
+ let json = r#"{"desired":{},"required":{}}"#;
+ let data = LegacyNewSessionParameters {
+ desired: Capabilities::new(),
+ required: Capabilities::new(),
+ };
+
+ check_deserialize(&json, &data);
+ }
-#[cfg(test)]
-mod tests {
- use rustc_serialize::json::Json;
- use super::{SpecNewSessionParameters, WebDriverResult};
+ #[test]
+ fn test_json_spec_legacy_new_session_parameters_both_with_capabilities() {
+ let json = r#"{"desired":{"foo":"bar"},"required":{"foo2":"bar2"}}"#;
+ let mut data = LegacyNewSessionParameters {
+ desired: Capabilities::new(),
+ required: Capabilities::new(),
+ };
+ data.desired.insert("foo".into(), "bar".into());
+ data.required.insert("foo2".into(), "bar2".into());
- fn validate_proxy(value: &str) -> WebDriverResult<()> {
- let data = Json::from_str(value).unwrap();
- SpecNewSessionParameters::validate_proxy(&data)
+ check_deserialize(&json, &data);
}
#[test]
fn test_validate_proxy() {
// proxy hosts
validate_proxy("{\"httpProxy\": \"127.0.0.1\"}").unwrap();
validate_proxy("{\"httpProxy\": \"127.0.0.1:\"}").unwrap();
validate_proxy("{\"httpProxy\": \"127.0.0.1:3128\"}").unwrap();
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -1,18 +1,18 @@
-use actions::{ActionSequence};
-use capabilities::{SpecNewSessionParameters, LegacyNewSessionParameters,
- CapabilitiesMatching, BrowserCapabilities, Capabilities};
-use common::{Date, Nullable, WebElement, FrameId, LocatorStrategy};
+use actions::ActionSequence;
+use capabilities::{
+ BrowserCapabilities, Capabilities, CapabilitiesMatching,
+ LegacyNewSessionParameters, SpecNewSessionParameters};
+use common::{Date, WebElement, FrameId, LocatorStrategy};
use error::{WebDriverResult, WebDriverError, ErrorStatus};
use httpapi::{Route, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
use regex::Captures;
-use rustc_serialize::json;
-use rustc_serialize::json::{ToJson, Json};
-use std::collections::BTreeMap;
+use serde::de::{self, Deserialize, Deserializer};
+use serde_json::{self, Value};
#[derive(Debug, PartialEq)]
pub enum WebDriverCommand<T: WebDriverExtensionCommand> {
NewSession(NewSessionParameters),
DeleteSession,
Get(GetParameters),
GetCurrentUrl,
GoBack,
@@ -60,30 +60,30 @@ pub enum WebDriverCommand<T: WebDriverEx
ElementSendKeys(WebElement, SendKeysParameters),
PerformActions(ActionsParameters),
ReleaseActions,
DismissAlert,
AcceptAlert,
GetAlertText,
SendAlertText(SendKeysParameters),
TakeScreenshot,
- TakeElementScreenshot(WebElement),
+ TakeElementScreenshot(TakeScreenshotParameters),
Status,
Extension(T)
}
pub trait WebDriverExtensionCommand : Clone + Send + PartialEq {
- fn parameters_json(&self) -> Option<Json>;
+ fn parameters_json(&self) -> Option<Value>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct VoidWebDriverExtensionCommand;
impl WebDriverExtensionCommand for VoidWebDriverExtensionCommand {
- fn parameters_json(&self) -> Option<Json> {
+ 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>,
@@ -100,82 +100,71 @@ impl<U: WebDriverExtensionRoute> WebDriv
}
pub fn from_http(match_type: Route<U>,
params: &Captures,
raw_body: &str,
requires_body: bool)
-> WebDriverResult<WebDriverMessage<U>> {
let session_id = WebDriverMessage::<U>::get_session_id(params);
- let body_data = try!(WebDriverMessage::<U>::decode_body(raw_body, requires_body));
-
+ let body_data = WebDriverMessage::<U>::decode_body(raw_body, requires_body)?;
let command = match match_type {
Route::NewSession => {
- let parameters: NewSessionParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::NewSession(parameters)
+ WebDriverCommand::NewSession(serde_json::from_str(raw_body)?)
},
Route::DeleteSession => WebDriverCommand::DeleteSession,
Route::Get => {
- let parameters: GetParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::Get(parameters)
+ 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::CloseWindow => WebDriverCommand::CloseWindow,
Route::GetTimeouts => WebDriverCommand::GetTimeouts,
Route::SetTimeouts => {
- let parameters: TimeoutsParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::SetTimeouts(parameters)
+ WebDriverCommand::SetTimeouts(serde_json::from_str(raw_body)?)
},
Route::GetWindowRect | Route::GetWindowPosition | Route::GetWindowSize => WebDriverCommand::GetWindowRect,
Route::SetWindowRect | Route::SetWindowPosition | Route::SetWindowSize => {
- let parameters: WindowRectParameters = Parameters::from_json(&body_data)?;
- WebDriverCommand::SetWindowRect(parameters)
+ WebDriverCommand::SetWindowRect(serde_json::from_str(raw_body)?)
},
Route::MinimizeWindow => WebDriverCommand::MinimizeWindow,
Route::MaximizeWindow => WebDriverCommand::MaximizeWindow,
Route::FullscreenWindow => WebDriverCommand::FullscreenWindow,
Route::SwitchToWindow => {
- let parameters: SwitchToWindowParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::SwitchToWindow(parameters)
+ WebDriverCommand::SwitchToWindow(serde_json::from_str(raw_body)?)
}
Route::SwitchToFrame => {
- let parameters: SwitchToFrameParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::SwitchToFrame(parameters)
+ WebDriverCommand::SwitchToFrame(serde_json::from_str(raw_body)?)
},
Route::SwitchToParentFrame => WebDriverCommand::SwitchToParentFrame,
Route::FindElement => {
- let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::FindElement(parameters)
+ WebDriverCommand::FindElement(serde_json::from_str(raw_body)?)
},
Route::FindElements => {
- let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::FindElements(parameters)
+ WebDriverCommand::FindElements(serde_json::from_str(raw_body)?)
},
Route::FindElementElement => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
- let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::FindElementElement(element, parameters)
+ WebDriverCommand::FindElementElement(element, serde_json::from_str(raw_body)?)
},
Route::FindElementElements => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
- let parameters: LocatorParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::FindElementElements(element, parameters)
+ WebDriverCommand::FindElementElements(element, serde_json::from_str(raw_body)?)
},
Route::GetActiveElement => WebDriverCommand::GetActiveElement,
Route::IsDisplayed => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::IsDisplayed(element)
@@ -266,872 +255,792 @@ impl<U: WebDriverExtensionRoute> WebDriv
let element = WebElement::new(element_id.as_str().into());
WebDriverCommand::ElementClear(element)
},
Route::ElementSendKeys => {
let element_id = try_opt!(params.name("elementId"),
ErrorStatus::InvalidArgument,
"Missing elementId parameter");
let element = WebElement::new(element_id.as_str().into());
- let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::ElementSendKeys(element, parameters)
+ WebDriverCommand::ElementSendKeys(element, serde_json::from_str(raw_body)?)
},
Route::ExecuteScript => {
- let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::ExecuteScript(parameters)
+ WebDriverCommand::ExecuteScript(serde_json::from_str(raw_body)?)
},
Route::ExecuteAsyncScript => {
- let parameters: JavascriptCommandParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::ExecuteAsyncScript(parameters)
+ WebDriverCommand::ExecuteAsyncScript(serde_json::from_str(raw_body)?)
},
Route::GetCookies => {
WebDriverCommand::GetCookies
},
Route::GetNamedCookie => {
let name = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing 'name' parameter").as_str().into();
WebDriverCommand::GetNamedCookie(name)
},
Route::AddCookie => {
- let parameters: AddCookieParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::AddCookie(parameters)
+ WebDriverCommand::AddCookie(serde_json::from_str(raw_body)?)
},
Route::DeleteCookies => {
WebDriverCommand::DeleteCookies
},
Route::DeleteCookie => {
let name = try_opt!(params.name("name"),
ErrorStatus::InvalidArgument,
"Missing name parameter").as_str().into();
WebDriverCommand::DeleteCookie(name)
},
Route::PerformActions => {
- let parameters: ActionsParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::PerformActions(parameters)
+ 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 => {
- let parameters: SendKeysParameters = try!(Parameters::from_json(&body_data));
- WebDriverCommand::SendAlertText(parameters)
+ WebDriverCommand::SendAlertText(serde_json::from_str(raw_body)?)
},
Route::TakeScreenshot => WebDriverCommand::TakeScreenshot,
Route::TakeElementScreenshot => {
- let element_id = try_opt!(params.name("elementId"),
- ErrorStatus::InvalidArgument,
- "Missing elementId parameter");
- let element = WebElement::new(element_id.as_str().into());
- WebDriverCommand::TakeElementScreenshot(element)
+ WebDriverCommand::TakeElementScreenshot(serde_json::from_str(raw_body)?)
},
Route::Status => WebDriverCommand::Status,
Route::Extension(ref extension) => {
try!(extension.command(params, &body_data))
}
};
Ok(WebDriverMessage::new(session_id, command))
}
fn get_session_id(params: &Captures) -> Option<String> {
params.name("sessionId").map(|x| x.as_str().into())
}
- fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Json> {
+ fn decode_body(body: &str, requires_body: bool) -> WebDriverResult<Value> {
if requires_body {
- match Json::from_str(body) {
- Ok(x @ Json::Object(_)) => Ok(x),
+ 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(json::ParserError::SyntaxError(_, line, col)) => {
- let msg = format!("Failed to decode request as JSON: \"{}\"", body);
- let stack = format!("Syntax error at :{}:{}", line, col);
- Err(WebDriverError::new_with_stack(ErrorStatus::InvalidArgument, msg, stack))
- }
- Err(json::ParserError::IoError(e)) => {
- Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- format!("I/O error whilst decoding body: {}", e)))
+ 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(Json::Null)
+ Ok(Value::Null)
}
}
}
-impl <U:WebDriverExtensionRoute> ToJson for WebDriverMessage<U> {
- fn to_json(&self) -> Json {
- let parameters = match self.command {
- WebDriverCommand::AcceptAlert |
- WebDriverCommand::CloseWindow |
- WebDriverCommand::ReleaseActions |
- WebDriverCommand::DeleteCookie(_) |
- WebDriverCommand::DeleteCookies |
- WebDriverCommand::DeleteSession |
- WebDriverCommand::DismissAlert |
- WebDriverCommand::ElementClear(_) |
- WebDriverCommand::ElementClick(_) |
- WebDriverCommand::ElementTap(_) |
- WebDriverCommand::GetActiveElement |
- WebDriverCommand::GetAlertText |
- WebDriverCommand::GetNamedCookie(_) |
- WebDriverCommand::GetCookies |
- WebDriverCommand::GetCSSValue(_, _) |
- WebDriverCommand::GetCurrentUrl |
- WebDriverCommand::GetElementAttribute(_, _) |
- WebDriverCommand::GetElementProperty(_, _) |
- WebDriverCommand::GetElementRect(_) |
- WebDriverCommand::GetElementTagName(_) |
- WebDriverCommand::GetElementText(_) |
- WebDriverCommand::GetPageSource |
- WebDriverCommand::GetTimeouts |
- WebDriverCommand::GetTitle |
- WebDriverCommand::GetWindowHandle |
- WebDriverCommand::GetWindowHandles |
- WebDriverCommand::GetWindowRect |
- WebDriverCommand::GoBack |
- WebDriverCommand::GoForward |
- WebDriverCommand::IsDisplayed(_) |
- WebDriverCommand::IsEnabled(_) |
- WebDriverCommand::IsSelected(_) |
- WebDriverCommand::MinimizeWindow |
- WebDriverCommand::MaximizeWindow |
- WebDriverCommand::FullscreenWindow |
- WebDriverCommand::NewSession(_) |
- WebDriverCommand::Refresh |
- WebDriverCommand::Status |
- WebDriverCommand::SwitchToParentFrame |
- WebDriverCommand::TakeElementScreenshot(_) |
- WebDriverCommand::TakeScreenshot => {
- None
- },
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct ActionsParameters {
+ pub actions: Vec<ActionSequence>
+}
- WebDriverCommand::AddCookie(ref x) => Some(x.to_json()),
- WebDriverCommand::ElementSendKeys(_, ref x) => Some(x.to_json()),
- WebDriverCommand::ExecuteAsyncScript(ref x) |
- WebDriverCommand::ExecuteScript(ref x) => Some(x.to_json()),
- WebDriverCommand::FindElementElement(_, ref x) => Some(x.to_json()),
- WebDriverCommand::FindElementElements(_, ref x) => Some(x.to_json()),
- WebDriverCommand::FindElement(ref x) => Some(x.to_json()),
- WebDriverCommand::FindElements(ref x) => Some(x.to_json()),
- WebDriverCommand::Get(ref x) => Some(x.to_json()),
- WebDriverCommand::PerformActions(ref x) => Some(x.to_json()),
- WebDriverCommand::SendAlertText(ref x) => Some(x.to_json()),
- WebDriverCommand::SetTimeouts(ref x) => Some(x.to_json()),
- WebDriverCommand::SetWindowRect(ref x) => Some(x.to_json()),
- WebDriverCommand::SwitchToFrame(ref x) => Some(x.to_json()),
- WebDriverCommand::SwitchToWindow(ref x) => Some(x.to_json()),
- WebDriverCommand::Extension(ref x) => x.parameters_json(),
- };
+#[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>,
+}
- let mut data = BTreeMap::new();
- if let Some(parameters) = parameters {
- data.insert("parameters".to_string(), parameters);
+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,
}
- Json::Object(data)
+
+ Wrapper::deserialize(deserializer).map(|wrapper| wrapper.cookie)
}
}
-pub trait Parameters: Sized {
- fn from_json(body: &Json) -> WebDriverResult<Self>;
+#[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
}
/// Wrapper around the two supported variants of new session paramters
///
/// The Spec variant is used for storing spec-compliant parameters whereas
/// the legacy variant is used to store desiredCapabilities/requiredCapabilities
/// parameters, and is intended to minimise breakage as we transition users to
/// the spec design.
+
#[derive(Debug, PartialEq)]
pub enum NewSessionParameters {
Spec(SpecNewSessionParameters),
Legacy(LegacyNewSessionParameters),
}
-impl Parameters for NewSessionParameters {
- fn from_json(body: &Json) -> WebDriverResult<NewSessionParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::UnknownError,
- "Message body was not an object");
- if data.get("capabilities").is_some() {
- Ok(NewSessionParameters::Spec(try!(SpecNewSessionParameters::from_json(body))))
- } else {
- Ok(NewSessionParameters::Legacy(try!(LegacyNewSessionParameters::from_json(body))))
+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)?;
+ if let Some(caps) = value.get("capabilities") {
+ let caps = SpecNewSessionParameters::deserialize(caps).map_err(de::Error::custom)?;
+ return Ok(NewSessionParameters::Spec(caps));
}
- }
-}
-
-impl ToJson for NewSessionParameters {
- fn to_json(&self) -> Json {
- match self {
- &NewSessionParameters::Spec(ref x) => x.to_json(),
- &NewSessionParameters::Legacy(ref x) => x.to_json()
- }
+ let legacy = LegacyNewSessionParameters::deserialize(value).map_err(de::Error::custom)?;
+ Ok(NewSessionParameters::Legacy(legacy))
}
}
impl CapabilitiesMatching for NewSessionParameters {
fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
-> WebDriverResult<Option<Capabilities>> {
match self {
- &NewSessionParameters::Spec(ref x) => x.match_browser(browser_capabilities),
+ &NewSessionParameters::Spec(ref x) =>
+ x.match_browser(browser_capabilities),
&NewSessionParameters::Legacy(ref x) => x.match_browser(browser_capabilities)
}
}
}
-
-#[derive(Debug, PartialEq)]
-pub struct GetParameters {
- pub url: String
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SendKeysParameters {
+ pub text: String
}
-impl Parameters for GetParameters {
- fn from_json(body: &Json) -> WebDriverResult<GetParameters> {
- let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
- "Message body was not an object");
- let url = try_opt!(
- try_opt!(data.get("url"),
- ErrorStatus::InvalidArgument,
- "Missing 'url' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'url' not a string");
- Ok(GetParameters {
- url: url.to_string()
- })
- }
-}
-
-impl ToJson for GetParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("url".to_string(), self.url.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct TimeoutsParameters {
- pub script: Option<u64>,
- pub page_load: Option<u64>,
- pub implicit: Option<u64>,
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SwitchToFrameParameters {
+ pub id: Option<FrameId>
}
-impl Parameters for TimeoutsParameters {
- fn from_json(body: &Json) -> WebDriverResult<TimeoutsParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::UnknownError,
- "Message body was not an object");
-
- let script = match data.get("script") {
- Some(json) => {
- Some(try_opt!(json.as_u64(),
- ErrorStatus::InvalidArgument,
- "Script timeout duration was not a signed integer"))
- }
- None => None,
- };
-
- let page_load = match data.get("pageLoad") {
- Some(json) => {
- Some(try_opt!(json.as_u64(),
- ErrorStatus::InvalidArgument,
- "Page load timeout duration was not a signed integer"))
- }
- None => None,
- };
-
- let implicit = match data.get("implicit") {
- Some(json) => {
- Some(try_opt!(json.as_u64(),
- ErrorStatus::InvalidArgument,
- "Implicit timeout duration was not a signed integer"))
- }
- None => None,
- };
-
- Ok(TimeoutsParameters {
- script: script,
- page_load: page_load,
- implicit: implicit,
- })
- }
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct SwitchToWindowParameters {
+ pub handle: String
}
-impl ToJson for TimeoutsParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- if let Some(ms) = self.script {
- data.insert("script".into(), ms.to_json());
- }
- if let Some(ms) = self.page_load {
- data.insert("pageLoad".into(), ms.to_json());
- }
- if let Some(ms) = self.implicit {
- data.insert("implicit".into(), ms.to_json());
- }
- Json::Object(data)
- }
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct TakeScreenshotParameters {
+ pub element: Option<WebElement>
+}
+
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
+pub struct TimeoutsParameters {
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub implicit: Option<u64>,
+ #[serde(rename = "pageLoad", skip_serializing_if = "Option::is_none")]
+ pub page_load: Option<u64>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub script: Option<u64>,
}
/// 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)]
+#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct WindowRectParameters {
- pub x: Nullable<i32>,
- pub y: Nullable<i32>,
- pub width: Nullable<i32>,
- pub height: Nullable<i32>,
-}
-
-impl Parameters for WindowRectParameters {
- fn from_json(body: &Json) -> WebDriverResult<WindowRectParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument, "Message body was not an object");
-
- let x = match data.get("x") {
- Some(json) => try!(Nullable::from_json(json, |n| {
- let x = try_opt!(
- n.as_f64(),
- ErrorStatus::InvalidArgument,
- "'x' is not a number"
- ) as i64;
- if x < i32::min_value() as i64 || x > i32::max_value() as i64 {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "'x' is larger than i32",
- ));
- }
- Ok(x as i32)
- })),
- None => Nullable::Null,
- };
-
- let y = match data.get("y") {
- Some(json) => try!(Nullable::from_json(json, |n| {
- let y = try_opt!(
- n.as_f64(),
- ErrorStatus::InvalidArgument,
- "'y' is not a number"
- ) as i64;
- if y < i32::min_value() as i64 || y > i32::max_value() as i64 {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "'y' is larger than i32",
- ));
- }
- Ok(y as i32)
- })),
- None => Nullable::Null,
- };
-
- let width = match data.get("width") {
- Some(json) => try!(Nullable::from_json(json, |n| {
- let width = try_opt!(
- n.as_f64(),
- ErrorStatus::InvalidArgument,
- "'width' is not a number"
- ) as i64;
- if width < 0 || width > i32::max_value() as i64 {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "'width' is larger than i32",
- ));
- }
- Ok(width as i32)
- })),
- None => Nullable::Null,
- };
-
- let height = match data.get("height") {
- Some(json) => try!(Nullable::from_json(json, |n| {
- let height = try_opt!(
- n.as_f64(),
- ErrorStatus::InvalidArgument,
- "'height' is not a positive integer"
- ) as i64;
- if height < 0 || height > i32::max_value() as i64 {
- return Err(WebDriverError::new(
- ErrorStatus::InvalidArgument,
- "'height' is larger than i32",
- ));
- }
- Ok(height as i32)
- })),
- None => Nullable::Null,
- };
-
- Ok(WindowRectParameters { x, y, width, height })
- }
-}
-
-impl ToJson for WindowRectParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("x".to_string(), self.x.to_json());
- data.insert("y".to_string(), self.y.to_json());
- data.insert("width".to_string(), self.width.to_json());
- data.insert("height".to_string(), self.height.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct SwitchToWindowParameters {
- pub handle: String
-}
-
-impl Parameters for SwitchToWindowParameters {
- fn from_json(body: &Json) -> WebDriverResult<SwitchToWindowParameters> {
- let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
- "Message body was not an object");
- let handle = try_opt!(
- try_opt!(data.get("handle"),
- ErrorStatus::InvalidArgument,
- "Missing 'handle' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'handle' not a string");
- return Ok(SwitchToWindowParameters {
- handle: handle.to_string()
- })
- }
-}
-
-impl ToJson for SwitchToWindowParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("handle".to_string(), self.handle.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct LocatorParameters {
- pub using: LocatorStrategy,
- pub value: String
-}
-
-impl Parameters for LocatorParameters {
- fn from_json(body: &Json) -> WebDriverResult<LocatorParameters> {
- let data = try_opt!(body.as_object(), ErrorStatus::UnknownError,
- "Message body was not an object");
-
- let using = try!(LocatorStrategy::from_json(
- try_opt!(data.get("using"),
- ErrorStatus::InvalidArgument,
- "Missing 'using' parameter")));
-
- let value = try_opt!(
- try_opt!(data.get("value"),
- ErrorStatus::InvalidArgument,
- "Missing 'value' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Could not convert using to string").to_string();
-
- return Ok(LocatorParameters {
- using: using,
- value: value
- })
- }
-}
-
-impl ToJson for LocatorParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("using".to_string(), self.using.to_json());
- data.insert("value".to_string(), self.value.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct SwitchToFrameParameters {
- pub id: FrameId
-}
-
-impl Parameters for SwitchToFrameParameters {
- fn from_json(body: &Json) -> WebDriverResult<SwitchToFrameParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::UnknownError,
- "Message body was not an object");
- let id = try!(FrameId::from_json(try_opt!(data.get("id"),
- ErrorStatus::UnknownError,
- "Missing 'id' parameter")));
-
- Ok(SwitchToFrameParameters {
- id: id
- })
- }
-}
-
-impl ToJson for SwitchToFrameParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("id".to_string(), self.id.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct SendKeysParameters {
- pub text: String
-}
-
-impl Parameters for SendKeysParameters {
- fn from_json(body: &Json) -> WebDriverResult<SendKeysParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Message body was not an object");
- let text = try_opt!(try_opt!(data.get("text"),
- ErrorStatus::InvalidArgument,
- "Missing 'text' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Could not convert 'text' to string");
-
- Ok(SendKeysParameters {
- text: text.into()
- })
- }
-}
-
-impl ToJson for SendKeysParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("value".to_string(), self.text.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct JavascriptCommandParameters {
- pub script: String,
- pub args: Nullable<Vec<Json>>
+ #[serde(default, deserialize_with = "deserialize_to_i32")]
+ pub x: Option<i32>,
+ #[serde(default, deserialize_with = "deserialize_to_i32")]
+ pub y: Option<i32>,
+ #[serde(default, deserialize_with = "deserialize_to_positive_i32")]
+ pub width: Option<i32>,
+ #[serde(default, deserialize_with = "deserialize_to_positive_i32")]
+ pub height: Option<i32>,
}
-impl Parameters for JavascriptCommandParameters {
- fn from_json(body: &Json) -> WebDriverResult<JavascriptCommandParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Message body was not an object");
-
- let args_json = try_opt!(data.get("args"),
- ErrorStatus::InvalidArgument,
- "Missing args parameter");
-
- let args = try!(Nullable::from_json(
- args_json,
- |x| {
- Ok((try_opt!(x.as_array(),
- ErrorStatus::InvalidArgument,
- "Failed to convert args to Array")).clone())
- }));
-
- //TODO: Look for WebElements in args?
- let script = try_opt!(
- try_opt!(data.get("script"),
- ErrorStatus::InvalidArgument,
- "Missing script parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "Failed to convert script to String");
- Ok(JavascriptCommandParameters {
- script: script.to_string(),
- args: args.clone()
- })
- }
-}
+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 < i32::min_value() as i64 || n > i32::max_value() as i64 {
+ return Err(de::Error::custom(format!("'{}' is larger than i32", n)));
+ }
+ Some(n as i32)
+ },
+ None => None
+ };
-impl ToJson for JavascriptCommandParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- //TODO: Wrap script so that it becomes marionette-compatible
- data.insert("script".to_string(), self.script.to_json());
- data.insert("args".to_string(), self.args.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct GetNamedCookieParameters {
- pub name: Nullable<String>,
-}
-
-impl Parameters for GetNamedCookieParameters {
- fn from_json(body: &Json) -> WebDriverResult<GetNamedCookieParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Message body was not an object");
- let name_json = try_opt!(data.get("name"),
- ErrorStatus::InvalidArgument,
- "Missing 'name' parameter");
- let name = try!(Nullable::from_json(name_json, |x| {
- Ok(try_opt!(x.as_string(),
- ErrorStatus::InvalidArgument,
- "Failed to convert name to string")
- .to_string())
- }));
- return Ok(GetNamedCookieParameters { name: name });
- }
-}
-
-impl ToJson for GetNamedCookieParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("name".to_string(), self.name.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct AddCookieParameters {
- pub name: String,
- pub value: String,
- pub path: Nullable<String>,
- pub domain: Nullable<String>,
- pub expiry: Nullable<Date>,
- pub secure: bool,
- pub httpOnly: bool
+ Ok(value)
}
-impl Parameters for AddCookieParameters {
- fn from_json(body: &Json) -> WebDriverResult<AddCookieParameters> {
- if !body.is_object() {
- return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- "Message body was not an object"));
- }
-
- let data = try_opt!(body.find("cookie").and_then(|x| x.as_object()),
- ErrorStatus::UnableToSetCookie,
- "Cookie parameter not found or not an object");
-
- let name = try_opt!(
- try_opt!(data.get("name"),
- ErrorStatus::InvalidArgument,
- "Missing 'name' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'name' is not a string").to_string();
-
- let value = try_opt!(
- try_opt!(data.get("value"),
- ErrorStatus::InvalidArgument,
- "Missing 'value' parameter").as_string(),
- ErrorStatus::InvalidArgument,
- "'value' is not a string").to_string();
-
- let path = match data.get("path") {
- Some(path_json) => {
- try!(Nullable::from_json(
- path_json,
- |x| {
- Ok(try_opt!(x.as_string(),
- ErrorStatus::InvalidArgument,
- "Failed to convert path to String").to_string())
- }))
- },
- None => Nullable::Null
- };
-
- let domain = match data.get("domain") {
- Some(domain_json) => {
- try!(Nullable::from_json(
- domain_json,
- |x| {
- Ok(try_opt!(x.as_string(),
- ErrorStatus::InvalidArgument,
- "Failed to convert domain to String").to_string())
- }))
- },
- None => Nullable::Null
- };
-
- let expiry = match data.get("expiry") {
- Some(expiry_json) => {
- try!(Nullable::from_json(
- expiry_json,
- |x| {
- Ok(Date::new(try_opt!(x.as_u64(),
- ErrorStatus::InvalidArgument,
- "Failed to convert expiry to Date")))
- }))
- },
- None => Nullable::Null
- };
-
- let secure = match data.get("secure") {
- Some(x) => try_opt!(x.as_boolean(),
- ErrorStatus::InvalidArgument,
- "Failed to convert secure to boolean"),
- None => false
- };
-
- let http_only = match data.get("httpOnly") {
- Some(x) => try_opt!(x.as_boolean(),
- ErrorStatus::InvalidArgument,
- "Failed to convert httpOnly to boolean"),
- None => false
- };
+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 > i32::max_value() as i64 {
+ return Err(de::Error::custom(format!("'{}' is outside of i32", n)));
+ }
+ Some(n as i32)
+ },
+ None => None
+ };
- return Ok(AddCookieParameters {
- name: name,
- value: value,
- path: path,
- domain: domain,
- expiry: expiry,
- secure: secure,
- httpOnly: http_only
- })
- }
-}
-
-impl ToJson for AddCookieParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("name".to_string(), self.name.to_json());
- data.insert("value".to_string(), self.value.to_json());
- data.insert("path".to_string(), self.path.to_json());
- data.insert("domain".to_string(), self.domain.to_json());
- data.insert("expiry".to_string(), self.expiry.to_json());
- data.insert("secure".to_string(), self.secure.to_json());
- data.insert("httpOnly".to_string(), self.httpOnly.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct TakeScreenshotParameters {
- pub element: Nullable<WebElement>
-}
-
-impl Parameters for TakeScreenshotParameters {
- fn from_json(body: &Json) -> WebDriverResult<TakeScreenshotParameters> {
- let data = try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Message body was not an object");
- let element = match data.get("element") {
- Some(element_json) => try!(Nullable::from_json(
- element_json,
- |x| {
- Ok(try!(WebElement::from_json(x)))
- })),
- None => Nullable::Null
- };
-
- return Ok(TakeScreenshotParameters {
- element: element
- })
- }
-}
-
-impl ToJson for TakeScreenshotParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("element".to_string(), self.element.to_json());
- Json::Object(data)
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub struct ActionsParameters {
- pub actions: Vec<ActionSequence>
-}
-
-impl Parameters for ActionsParameters {
- fn from_json(body: &Json) -> WebDriverResult<ActionsParameters> {
- try_opt!(body.as_object(),
- ErrorStatus::InvalidArgument,
- "Message body was not an object");
- let actions = try_opt!(
- try_opt!(body.find("actions"),
- ErrorStatus::InvalidArgument,
- "No actions parameter found").as_array(),
- ErrorStatus::InvalidArgument,
- "Parameter 'actions' was not an array");
-
- let mut result = Vec::with_capacity(actions.len());
- for chain in actions.iter() {
- result.push(try!(ActionSequence::from_json(chain)));
- }
- Ok(ActionsParameters {
- actions: result
- })
- }
-}
-
-impl ToJson for ActionsParameters {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("actions".to_owned(),
- self.actions.iter().map(|x| x.to_json()).collect::<Vec<Json>>().to_json());
- Json::Object(data)
- }
+ Ok(value)
}
#[cfg(test)]
mod tests {
- use rustc_serialize::json::Json;
- use super::{Nullable, Parameters, WindowRectParameters};
+ use super::*;
+ use capabilities::SpecNewSessionParameters;
+ use serde_json;
+ use test::check_deserialize;
+
+ #[test]
+ fn test_json_actions_parameters_missing_actions_field() {
+ let json = r#"{}"#;
+ assert!(serde_json::from_str::<ActionsParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_actions_parameters_invalid() {
+ let json = r#"{"actions":null}"#;
+ assert!(serde_json::from_str::<ActionsParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_action_parameters_empty_list() {
+ let json = r#"{"actions":[]}"#;
+ let data = ActionsParameters { actions: vec![] };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_add_cookie_parameters_with_values() {
+ let json = r#"{"cookie":{
+ "name":"foo",
+ "value":"bar",
+ "path":"/",
+ "domain":"foo.bar",
+ "expiry":123,
+ "secure":true,
+ "httpOnly":false
+ }}"#;
+ let data = AddCookieParameters {
+ name: "foo".into(),
+ value: "bar".into(),
+ path: Some("/".into()),
+ domain: Some("foo.bar".into()),
+ expiry: Some(Date::new(123)),
+ secure: true,
+ httpOnly: false,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_add_cookie_parameters_with_optional_null_fields() {
+ let json = r#"{"cookie":{
+ "name":"foo",
+ "value":"bar",
+ "path":null,
+ "domain":null,
+ "expiry":null,
+ "secure":true,
+ "httpOnly":false
+ }}"#;
+ let data = AddCookieParameters {
+ name: "foo".into(),
+ value: "bar".into(),
+ path: None,
+ domain: None,
+ expiry: None,
+ secure: true,
+ httpOnly: false,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_add_cookie_parameters_without_optional_fields() {
+ let json = r#"{"cookie":{
+ "name":"foo",
+ "value":"bar",
+ "secure":true,
+ "httpOnly":false
+ }}"#;
+ let data = AddCookieParameters {
+ name: "foo".into(),
+ value: "bar".into(),
+ path: None,
+ domain: None,
+ expiry: None,
+ secure: true,
+ httpOnly: false,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_add_cookie_parameters_with_invalid_cookie_field() {
+ let json = r#"{"name":"foo"}"#;
+
+ assert!(serde_json::from_str::<AddCookieParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_get_parameters_with_url() {
+ let json = r#"{"url":"foo.bar"}"#;
+ let data = GetParameters { url: "foo.bar".into() };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_get_parameters_with_invalid_url_value() {
+ let json = r#"{"url":3}"#;
+
+ assert!(serde_json::from_str::<GetParameters>(&json).is_err());
+ }
#[test]
- fn test_window_rect() {
- let expected = WindowRectParameters {
- x: Nullable::Value(0i32),
- y: Nullable::Value(1i32),
- width: Nullable::Value(2i32),
- height: Nullable::Value(3i32),
+ fn test_json_get_parameters_with_invalid_url_field() {
+ let json = r#"{"foo":"bar"}"#;
+
+ assert!(serde_json::from_str::<GetParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_get_named_cookie_parameters_with_value() {
+ let json = r#"{"name":"foo"}"#;
+ let data = GetNamedCookieParameters { name: Some("foo".into()) };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_get_named_cookie_parameters_with_optional_null_field() {
+ let json = r#"{"name":null}"#;
+ let data = GetNamedCookieParameters { name: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_get_named_cookie_parameters_without_optional_null_field() {
+ let json = r#"{}"#;
+ let data = GetNamedCookieParameters { name: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_get_named_cookie_parameters_with_invalid_name_field() {
+ let json = r#"{"name":3"#;
+
+ assert!(serde_json::from_str::<GetNamedCookieParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_with_values() {
+ let json = r#"{"script":"foo","args":["1",2]}"#;
+ let data = JavascriptCommandParameters {
+ script: "foo".into(),
+ args: Some(vec!("1".into(), 2.into())),
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_with_optional_null_field() {
+ let json = r#"{"script":"foo","args":null}"#;
+ let data = JavascriptCommandParameters {
+ script: "foo".into(),
+ args: None,
};
- let actual = Json::from_str(r#"{"x": 0, "y": 1, "width": 2, "height": 3}"#).unwrap();
- assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_without_optional_null_field() {
+ let json = r#"{"script":"foo"}"#;
+ let data = JavascriptCommandParameters {
+ script: "foo".into(),
+ args: None,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_invalid_script_field() {
+ let json = r#"{"script":null}"#;
+
+ assert!(serde_json::from_str::<JavascriptCommandParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_invalid_args_field() {
+ let json = r#"{"script":null,"args":"1"}"#;
+
+ assert!(serde_json::from_str::<JavascriptCommandParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_javascript_command_parameters_missing_script_field() {
+ let json = r#"{"args":null}"#;
+
+ assert!(serde_json::from_str::<JavascriptCommandParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_locator_parameters_with_values() {
+ let json = r#"{"using":"xpath","value":"bar"}"#;
+ let data = LocatorParameters {
+ using: LocatorStrategy::XPath,
+ value: "bar".into(),
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_locator_parameters_invalid_using_field() {
+ let json = r#"{"using":"foo","value":"bar"}"#;
+
+ assert!(serde_json::from_str::<LocatorParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_locator_parameters_invalid_value_field() {
+ let json = r#"{"using":"xpath","value":3}"#;
+
+ assert!(serde_json::from_str::<LocatorParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_locator_parameters_missing_using_field() {
+ let json = r#"{"value":"bar"}"#;
+
+ assert!(serde_json::from_str::<LocatorParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_locator_parameters_missing_value_field() {
+ let json = r#"{"using":"xpath"}"#;
+
+ assert!(serde_json::from_str::<LocatorParameters>(&json).is_err());
}
#[test]
- fn test_window_rect_nullable() {
- let expected = WindowRectParameters {
- x: Nullable::Value(0i32),
- y: Nullable::Null,
- width: Nullable::Value(2i32),
- height: Nullable::Null,
- };
- let actual = Json::from_str(r#"{"x": 0, "y": null, "width": 2, "height": null}"#).unwrap();
- assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+ fn test_json_new_session_parameters_spec() {
+ let json = r#"{"capabilities":{"alwaysMatch":{},"firstMatch":[{}]}}"#;
+ let data = NewSessionParameters::Spec(SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() },
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_new_session_parameters_capabilities_null() {
+ let json = r#"{"capabilities":null}"#;
+
+ assert!(serde_json::from_str::<NewSessionParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_new_session_parameters_legacy() {
+ let json = r#"{"desired":{},"required":{}}"#;
+ let data = NewSessionParameters::Legacy(LegacyNewSessionParameters {
+ desired: Capabilities::new(),
+ required: Capabilities::new(),
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_new_session_parameters_spec_and_legacy() {
+ let json = r#"{
+ "capabilities":{
+ "alwaysMatch":{},
+ "firstMatch":[{}]
+ },
+ "desired":{},
+ "required":{}
+ }"#;
+ let data = NewSessionParameters::Spec(SpecNewSessionParameters {
+ alwaysMatch: Capabilities::new(),
+ firstMatch: vec!{ Capabilities::new() },
+ });
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_send_keys_parameters_with_value() {
+ let json = r#"{"text":"foo"}"#;
+ let data = SendKeysParameters { text: "foo".into() };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_send_keys_parameters_invalid_text_field() {
+ let json = r#"{"text":3}"#;
+
+ assert!(serde_json::from_str::<SendKeysParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_send_keys_parameters_missing_text_field() {
+ let json = r#"{}"#;
+
+ assert!(serde_json::from_str::<SendKeysParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_switch_to_frame_parameters_with_value() {
+ let json = r#"{"id":3}"#;
+ let data = SwitchToFrameParameters {
+ id: Some(FrameId::Short(3)) };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_switch_to_frame_parameters_with_optional_null_field() {
+ let json = r#"{"id":null}"#;
+ let data = SwitchToFrameParameters { id: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_switch_to_frame_parameters_without_optional_null_field() {
+ let json = r#"{}"#;
+ let data = SwitchToFrameParameters { id: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_switch_to_frame_parameters_with_invalid_id_field() {
+ let json = r#"{"id":"3""#;
+
+ assert!(serde_json::from_str::<SwitchToFrameParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_switch_to_window_parameters_with_value() {
+ let json = r#"{"handle":"foo"}"#;
+ let data = SwitchToWindowParameters { handle: "foo".into() };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_switch_to_window_parameters_invalid_handle_field() {
+ let json = r#"{"handle":3}"#;
+
+ assert!(serde_json::from_str::<SwitchToWindowParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_switch_to_window_parameters_missing_handle_field() {
+ let json = r#"{}"#;
+
+ assert!(serde_json::from_str::<SwitchToWindowParameters>(&json).is_err());
}
#[test]
- fn test_window_rect_missing_fields() {
- let expected = WindowRectParameters {
- x: Nullable::Value(0i32),
- y: Nullable::Null,
- width: Nullable::Value(2i32),
- height: Nullable::Null,
+ fn test_json_take_screenshot_parameters_with_element() {
+ let json = r#"{"element":{"element-6066-11e4-a52e-4f735466cecf":"elem"}}"#;
+ let data = TakeScreenshotParameters {
+ element: Some(WebElement { id: "elem".into() }),
};
- let actual = Json::from_str(r#"{"x": 0, "width": 2}"#).unwrap();
- assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_take_screenshot_parameters_with_optional_null_field() {
+ let json = r#"{"element":null}"#;
+ let data = TakeScreenshotParameters { element: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_take_screenshot_parameters_without_optional_null_field() {
+ let json = r#"{}"#;
+ let data = TakeScreenshotParameters { element: None };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_take_screenshot_parameters_with_invalid_element_field() {
+ let json = r#"{"element":"foo"}"#;
+ assert!(serde_json::from_str::<TakeScreenshotParameters>(&json).is_err());
+ }
+
+
+ #[test]
+ fn test_json_timeout_parameters_with_values() {
+ let json = r#"{"implicit":1,"pageLoad":2,"script":3}"#;
+ let data = TimeoutsParameters {
+ implicit: Some(1u64),
+ page_load: Some(2u64),
+ script: Some(3u64),
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_timeout_parameters_with_optional_null_field() {
+ let json = r#"{"implicit":null,"pageLoad":null,"script":null}"#;
+ let data = TimeoutsParameters {
+ implicit: None,
+ page_load: None,
+ script: None,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_timeout_parameters_without_optional_null_field() {
+ let json = r#"{}"#;
+ let data = TimeoutsParameters {
+ implicit: None,
+ page_load: None,
+ script: None,
+ };
+
+ check_deserialize(&json, &data);
}
#[test]
- fn test_window_rect_floats() {
- let expected = WindowRectParameters {
- x: Nullable::Value(1i32),
- y: Nullable::Value(2i32),
- width: Nullable::Value(3i32),
- height: Nullable::Value(4i32),
+ fn test_json_timeout_parameters_with_invalid_implicit_value() {
+ let json = r#"{"implicit":1.1}"#;
+ assert!(serde_json::from_str::<TimeoutsParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_timeout_parameters_with_invalid_page_load_value() {
+ let json = r#"{"pageLoad":1.2}"#;
+ assert!(serde_json::from_str::<TimeoutsParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_timeout_parameters_with_invalid_script_value() {
+ let json = r#"{"script":1.3}"#;
+ assert!(serde_json::from_str::<TimeoutsParameters>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_window_rect_parameters_with_values() {
+ let json = r#"{"x":0,"y":1,"width":2,"height":3}"#;
+ let data = WindowRectParameters {
+ x: Some(0i32),
+ y: Some(1i32),
+ width: Some(2i32),
+ height: Some(3i32),
};
- let actual = Json::from_str(r#"{"x": 1.1, "y": 2.2, "width": 3.3, "height": 4.4}"#).unwrap();
- assert_eq!(expected, Parameters::from_json(&actual).unwrap());
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_window_rect_parameters_with_optional_null_fields() {
+ let json = r#"{"x":null,"y": null,"width":null,"height":null}"#;
+ let data = WindowRectParameters {
+ x: None,
+ y: None,
+ width: None,
+ height: None,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_window_rect_parameters_without_optional_fields() {
+ let json = r#"{}"#;
+ let data = WindowRectParameters {
+ x: None,
+ y: None,
+ width: None,
+ height: None,
+ };
+
+ check_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_window_rect_parameters_invalid_values_float() {
+ let json = r#"{"x":1.1,"y":2.2,"width":3.3,"height":4.4}"#;
+ let data = WindowRectParameters {
+ x: Some(1),
+ y: Some(2),
+ width: Some(3),
+ height: Some(4),
+ };
+
+ check_deserialize(&json, &data);
}
}
--- a/testing/webdriver/src/common.rs
+++ b/testing/webdriver/src/common.rs
@@ -1,224 +1,177 @@
-use rustc_serialize::{Encodable, Encoder};
-use rustc_serialize::json::{Json, ToJson};
-use std::collections::BTreeMap;
-
-use error::{WebDriverResult, WebDriverError, ErrorStatus};
+use serde::ser::{Serialize, Serializer};
pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf";
pub static FRAME_KEY: &'static str = "frame-075b-4da1-b6ba-e579c2d3230a";
pub static WINDOW_KEY: &'static str = "window-fcc6-11e5-b4f8-330a88ab9d7f";
-#[derive(Clone, Debug, PartialEq, RustcEncodable)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub struct Cookie {
+ 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>,
+}
+
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Date(pub u64);
impl Date {
pub fn new(timestamp: u64) -> Date {
Date(timestamp)
}
}
-impl ToJson for Date {
- fn to_json(&self) -> Json {
- let &Date(x) = self;
- x.to_json()
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
-pub enum Nullable<T: ToJson> {
- Value(T),
- Null
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+#[serde(untagged)]
+pub enum FrameId {
+ Short(u16),
+ #[serde(
+ rename = "element-6066-11e4-a52e-4f735466cecf",
+ serialize_with = "serialize_webelement_id",
+ )]
+ Element(WebElement)
}
-impl<T: ToJson> Nullable<T> {
- pub fn is_null(&self) -> bool {
- match *self {
- Nullable::Value(_) => false,
- Nullable::Null => true
- }
- }
-
- pub fn is_value(&self) -> bool {
- match *self {
- Nullable::Value(_) => true,
- Nullable::Null => false
- }
- }
-
- pub fn map<F, U: ToJson>(self, f: F) -> Nullable<U>
- where F: FnOnce(T) -> U {
- match self {
- Nullable::Value(val) => Nullable::Value(f(val)),
- Nullable::Null => Nullable::Null
- }
- }
+// TODO(Henrik): Remove when ToMarionette trait has been fixed
+fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
+ where S: Serializer
+{
+ element.id.serialize(serializer)
}
-impl<T: ToJson> Nullable<T> {
- //This is not very pretty
- pub fn from_json<F: FnOnce(&Json) -> WebDriverResult<T>>(value: &Json, f: F) -> WebDriverResult<Nullable<T>> {
- if value.is_null() {
- Ok(Nullable::Null)
- } else {
- Ok(Nullable::Value(try!(f(value))))
- }
- }
-}
-
-impl<T: ToJson> ToJson for Nullable<T> {
- fn to_json(&self) -> Json {
- match *self {
- Nullable::Value(ref x) => x.to_json(),
- Nullable::Null => Json::Null
- }
- }
+#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
+pub enum LocatorStrategy {
+ #[serde(rename = "css selector")]
+ CSSSelector,
+ #[serde(rename = "link text")]
+ LinkText,
+ #[serde(rename = "partial link text")]
+ PartialLinkText,
+ #[serde(rename = "tag name")]
+ TagName,
+ #[serde(rename = "xpath")]
+ XPath,
}
-impl<T: ToJson> Encodable for Nullable<T> {
- fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
- match *self {
- Nullable::Value(ref x) => x.to_json().encode(s),
- Nullable::Null => s.emit_option_none()
- }
- }
-}
-
-impl<T: ToJson> Into<Option<T>> for Nullable<T> {
- fn into(self) -> Option<T> {
- match self {
- Nullable::Value(val) => Some(val),
- Nullable::Null => None
- }
- }
-}
-
-impl<T: ToJson> From<Option<T>> for Nullable<T> {
- fn from(option: Option<T>) -> Nullable<T> {
- match option {
- Some(val) => Nullable::Value(val),
- None => Nullable::Null,
- }
- }
-}
-
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WebElement {
+ #[serde(rename="element-6066-11e4-a52e-4f735466cecf")]
pub id: String
}
impl WebElement {
pub fn new(id: String) -> WebElement {
WebElement {
id: id
}
}
-
- pub fn from_json(data: &Json) -> WebDriverResult<WebElement> {
- let object = try_opt!(data.as_object(),
- ErrorStatus::InvalidArgument,
- "Could not convert webelement to object");
- let id_value = try_opt!(object.get(ELEMENT_KEY),
- ErrorStatus::InvalidArgument,
- "Could not find webelement key");
-
- let id = try_opt!(id_value.as_string(),
- ErrorStatus::InvalidArgument,
- "Could not convert web element to string").to_string();
-
- Ok(WebElement::new(id))
- }
-}
-
-impl ToJson for WebElement {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert(ELEMENT_KEY.to_string(), self.id.to_json());
- Json::Object(data)
- }
-}
-
-impl <T> From<T> for WebElement
- where T: Into<String> {
- fn from(data: T) -> WebElement {
- WebElement::new(data.into())
- }
-}
-
-#[derive(Debug, PartialEq)]
-pub enum FrameId {
- Short(u16),
- Element(WebElement),
- Null
}
-impl FrameId {
- pub fn from_json(data: &Json) -> WebDriverResult<FrameId> {
- match data {
- &Json::U64(x) => {
- if x > u16::max_value() as u64 || x < u16::min_value() as u64 {
- return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
- "frame id out of range"))
- };
- Ok(FrameId::Short(x as u16))
- },
- &Json::Null => Ok(FrameId::Null),
- &Json::Object(_) => Ok(FrameId::Element(
- try!(WebElement::from_json(data)))),
- _ => Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
- "frame id has unexpected type"))
- }
+#[cfg(test)]
+mod tests {
+ use serde_json;
+ use super::*;
+ use test::check_serialize_deserialize;
+
+ #[test]
+ fn test_json_date() {
+ let json = r#"1234"#;
+ let data = Date(1234);
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_date_invalid() {
+ let json = r#""2018-01-01""#;
+ assert!(serde_json::from_str::<Date>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_frame_id_short() {
+ let json = r#"1234"#;
+ let data = FrameId::Short ( 1234 );
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_frame_id_webelement() {
+ let json = r#""elem""#;
+ let data = FrameId::Element ( WebElement::new("elem".into()) );
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_frame_id_invalid() {
+ let json = r#"true"#;
+ assert!(serde_json::from_str::<FrameId>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_locator_strategy_css_selector() {
+ let json = r#""css selector""#;
+ let data = LocatorStrategy::CSSSelector;
+
+ check_serialize_deserialize(&json, &data);
}
-}
+
+ #[test]
+ fn test_json_locator_strategy_link_text() {
+ let json = r#""link text""#;
+ let data = LocatorStrategy::LinkText;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_locator_strategy_partial_link_text() {
+ let json = r#""partial link text""#;
+ let data = LocatorStrategy::PartialLinkText;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_locator_strategy_tag_name() {
+ let json = r#""tag name""#;
+ let data = LocatorStrategy::TagName;
+
+ check_serialize_deserialize(&json, &data);
+ }
-impl ToJson for FrameId {
- fn to_json(&self) -> Json {
- match *self {
- FrameId::Short(x) => {
- Json::U64(x as u64)
- },
- FrameId::Element(ref x) => {
- Json::String(x.id.clone())
- },
- FrameId::Null => {
- Json::Null
- }
- }
+ #[test]
+ fn test_json_locator_strategy_xpath() {
+ let json = r#""xpath""#;
+ let data = LocatorStrategy::XPath;
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_locator_strategy_invalid() {
+ let json = r#""foo""#;
+ assert!(serde_json::from_str::<LocatorStrategy>(&json).is_err());
+ }
+
+ #[test]
+ fn test_json_webelement() {
+ let json = r#"{"element-6066-11e4-a52e-4f735466cecf":"elem"}"#;
+ let data = WebElement::new("elem".into());
+
+ check_serialize_deserialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_webelement_invalid() {
+ let data = r#"{"elem-6066-11e4-a52e-4f735466cecf":"elem"}"#;
+ assert!(serde_json::from_str::<WebElement>(&data).is_err());
}
}
-
-#[derive(Debug, PartialEq)]
-pub enum LocatorStrategy {
- CSSSelector,
- LinkText,
- PartialLinkText,
- TagName,
- XPath,
-}
-
-impl LocatorStrategy {
- pub fn from_json(body: &Json) -> WebDriverResult<LocatorStrategy> {
- match try_opt!(body.as_string(),
- ErrorStatus::InvalidArgument,
- "Expected locator strategy as string") {
- "css selector" => Ok(LocatorStrategy::CSSSelector),
- "link text" => Ok(LocatorStrategy::LinkText),
- "partial link text" => Ok(LocatorStrategy::PartialLinkText),
- "tag name" => Ok(LocatorStrategy::TagName),
- "xpath" => Ok(LocatorStrategy::XPath),
- x => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
- format!("Unknown locator strategy {}", x)))
- }
- }
-}
-
-impl ToJson for LocatorStrategy {
- fn to_json(&self) -> Json {
- Json::String(match *self {
- LocatorStrategy::CSSSelector => "css selector",
- LocatorStrategy::LinkText => "link text",
- LocatorStrategy::PartialLinkText => "partial link text",
- LocatorStrategy::TagName => "tag name",
- LocatorStrategy::XPath => "xpath"
- }.to_string())
- }
-}
--- a/testing/webdriver/src/error.rs
+++ b/testing/webdriver/src/error.rs
@@ -1,13 +1,13 @@
use hyper::status::StatusCode;
-use rustc_serialize::base64::FromBase64Error;
-use rustc_serialize::json::{DecoderError, Json, ParserError, ToJson};
+use base64::DecodeError;
+use serde::ser::{Serialize, Serializer};
+use serde_json;
use std::borrow::Cow;
-use std::collections::BTreeMap;
use std::convert::From;
use std::error::Error;
use std::fmt;
use std::io;
#[derive(Debug, PartialEq)]
pub enum ErrorStatus {
/// The [`ElementClick`] command could not be completed because the
@@ -135,16 +135,25 @@ pub enum ErrorStatus {
UnknownPath,
/// Indicates that a [command] that should have executed properly is not
/// currently supported.
UnsupportedOperation,
}
+impl Serialize for ErrorStatus {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.error_code().serialize(serializer)
+ }
+}
+
impl ErrorStatus {
/// Returns the string serialisation of the error type.
pub fn error_code(&self) -> &'static str {
use self::ErrorStatus::*;
match *self {
ElementClickIntercepted => "element click intercepted",
ElementNotInteractable => "element not interactable",
ElementNotSelectable => "element not selectable",
@@ -248,24 +257,42 @@ impl From<String> for ErrorStatus {
"unsupported operation" => UnsupportedOperation,
_ => UnknownError,
}
}
}
pub type WebDriverResult<T> = Result<T, WebDriverError>;
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Serialize)]
+#[serde(remote = "Self")]
pub struct WebDriverError {
pub error: ErrorStatus,
pub message: Cow<'static, str>,
+ #[serde(rename = "stacktrace")]
pub stack: Cow<'static, str>,
+ #[serde(skip)]
pub delete_session: bool,
}
+impl Serialize for WebDriverError {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ #[derive(Serialize)]
+ struct Wrapper<'a> {
+ #[serde(with = "WebDriverError")]
+ value: &'a WebDriverError,
+ }
+
+ Wrapper { value: self }.serialize(serializer)
+ }
+}
+
impl WebDriverError {
pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
where S: Into<Cow<'static, str>>
{
WebDriverError {
error: error,
message: message.into(),
stack: "".into(),
@@ -286,33 +313,16 @@ impl WebDriverError {
pub fn error_code(&self) -> &'static str {
self.error.error_code()
}
pub fn http_status(&self) -> StatusCode {
self.error.http_status()
}
-
- pub fn to_json_string(&self) -> String {
- self.to_json().to_string()
- }
-}
-
-impl ToJson for WebDriverError {
- fn to_json(&self) -> Json {
- let mut data = BTreeMap::new();
- data.insert("error".into(), self.error_code().to_json());
- data.insert("message".into(), self.message.to_json());
- data.insert("stacktrace".into(), self.stack.to_json());
-
- let mut wrapper = BTreeMap::new();
- wrapper.insert("value".into(), Json::Object(data));
- Json::Object(wrapper)
- }
}
impl Error for WebDriverError {
fn description(&self) -> &str {
self.error_code()
}
fn cause(&self) -> Option<&Error> {
@@ -321,37 +331,62 @@ impl Error for WebDriverError {
}
impl fmt::Display for WebDriverError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.message.fmt(f)
}
}
-impl From<ParserError> for WebDriverError {
- fn from(err: ParserError) -> WebDriverError {
- WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
+impl From<serde_json::Error> for WebDriverError {
+ fn from(err: serde_json::Error) -> WebDriverError {
+ WebDriverError::new(ErrorStatus::InvalidArgument, err.to_string())
}
}
impl From<io::Error> for WebDriverError {
fn from(err: io::Error) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
}
}
-impl From<DecoderError> for WebDriverError {
- fn from(err: DecoderError) -> WebDriverError {
- WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
- }
-}
-
-impl From<FromBase64Error> for WebDriverError {
- fn from(err: FromBase64Error) -> WebDriverError {
+impl From<DecodeError> for WebDriverError {
+ fn from(err: DecodeError) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
}
}
impl From<Box<Error>> for WebDriverError {
fn from(err: Box<Error>) -> WebDriverError {
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use test::check_serialize;
+
+ #[test]
+ fn test_json_webdriver_error() {
+ let json = r#"{"value":{
+ "error":"unknown error",
+ "message":"foo bar",
+ "stacktrace":"foo\nbar"
+ }}"#;
+ let data = WebDriverError {
+ error: ErrorStatus::UnknownError,
+ message: "foo bar".into(),
+ stack: "foo\nbar".into(),
+ delete_session: true,
+ };
+
+ check_serialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_error_status() {
+ let json = format!(r#""unknown error""#);
+ let data = ErrorStatus::UnknownError;
+
+ check_serialize(&json, &data);
+ }
+}
\ No newline at end of file
--- a/testing/webdriver/src/httpapi.rs
+++ b/testing/webdriver/src/httpapi.rs
@@ -1,13 +1,13 @@
use regex::{Regex, Captures};
-use rustc_serialize::json::Json;
use hyper::method::Method;
use hyper::method::Method::{Get, Post, Delete};
+use serde_json::Value;
use command::{WebDriverCommand, WebDriverMessage, WebDriverExtensionCommand,
VoidWebDriverExtensionCommand};
use error::{WebDriverResult, WebDriverError, ErrorStatus};
fn standard_routes<U:WebDriverExtensionRoute>() -> Vec<(Method, &'static str, Route<U>)> {
return vec![(Post, "/session", Route::NewSession),
(Delete, "/session/{sessionId}", Route::DeleteSession),
@@ -134,26 +134,26 @@ pub enum Route<U:WebDriverExtensionRoute
TakeElementScreenshot,
Status,
Extension(U),
}
pub trait WebDriverExtensionRoute : Clone + Send + PartialEq {
type Command: WebDriverExtensionCommand + 'static;
- fn command(&self, &Captures, &Json) -> WebDriverResult<WebDriverCommand<Self::Command>>;
+ fn command(&self, &Captures, &Value) -> WebDriverResult<WebDriverCommand<Self::Command>>;
}
#[derive(Clone, Debug, PartialEq)]
pub struct VoidWebDriverExtensionRoute;
impl WebDriverExtensionRoute for VoidWebDriverExtensionRoute {
type Command = VoidWebDriverExtensionCommand;
- fn command(&self, _:&Captures, _:&Json) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
+ fn command(&self, _:&Captures, _:&Value) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
panic!("No extensions implemented");
}
}
#[derive(Clone, Debug)]
struct RequestMatcher<U: WebDriverExtensionRoute> {
method: Method,
path_regexp: Regex,
--- a/testing/webdriver/src/lib.rs
+++ b/testing/webdriver/src/lib.rs
@@ -1,44 +1,28 @@
#![allow(non_snake_case)]
+extern crate cookie;
+extern crate base64;
+#[macro_use]
+extern crate lazy_static;
#[macro_use]
extern crate log;
-extern crate rustc_serialize;
extern crate hyper;
extern crate regex;
-extern crate cookie;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_json;
extern crate time;
extern crate url;
extern crate unicode_segmentation;
#[macro_use] pub mod macros;
pub mod actions;
pub mod httpapi;
pub mod capabilities;
pub mod command;
pub mod common;
pub mod error;
pub mod server;
pub mod response;
-
-#[cfg(test)]
-mod nullable_tests {
- use super::common::Nullable;
-
- #[test]
- fn test_nullable_map() {
- let mut test = Nullable::Value(21);
-
- assert_eq!(test.map(|x| x << 1), Nullable::Value(42));
-
- test = Nullable::Null;
-
- assert_eq!(test.map(|x| x << 1), Nullable::Null);
- }
-
- #[test]
- fn test_nullable_into() {
- let test: Option<i32> = Nullable::Value(42).into();
-
- assert_eq!(test, Some(42));
- }
-}
+pub mod test;
--- a/testing/webdriver/src/macros.rs
+++ b/testing/webdriver/src/macros.rs
@@ -1,8 +1,7 @@
macro_rules! try_opt {
($expr:expr, $err_type:expr, $err_msg:expr) => ({
match $expr {
Some(x) => x,
None => return Err(WebDriverError::new($err_type, $err_msg))
}
- })
-}
+ })}
--- a/testing/webdriver/src/response.rs
+++ b/testing/webdriver/src/response.rs
@@ -1,124 +1,91 @@
-use common::{Date, Nullable};
-use cookie;
-use rustc_serialize::json::{self, Json, ToJson};
-use std::collections::BTreeMap;
-use time;
+use common::Cookie;
+use serde::ser::{Serialize, Serializer};
+use serde_json::Value;
-#[derive(Debug)]
+#[derive(Debug, PartialEq, Serialize)]
+#[serde(untagged, remote = "Self")]
pub enum WebDriverResponse {
CloseWindow(CloseWindowResponse),
Cookie(CookieResponse),
Cookies(CookiesResponse),
DeleteSession,
ElementRect(ElementRectResponse),
Generic(ValueResponse),
NewSession(NewSessionResponse),
Timeouts(TimeoutsResponse),
Void,
WindowRect(WindowRectResponse),
}
-impl WebDriverResponse {
- pub fn to_json_string(self) -> String {
- use response::WebDriverResponse::*;
+impl Serialize for WebDriverResponse {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ #[derive(Serialize)]
+ struct Wrapper<'a> {
+ #[serde(with = "WebDriverResponse")]
+ value: &'a WebDriverResponse,
+ }
- let obj = match self {
- CloseWindow(ref x) => json::encode(&x.to_json()),
- Cookie(ref x) => json::encode(x),
- Cookies(ref x) => json::encode(x),
- DeleteSession => Ok("null".to_string()),
- ElementRect(ref x) => json::encode(x),
- Generic(ref x) => json::encode(x),
- NewSession(ref x) => json::encode(x),
- Timeouts(ref x) => json::encode(x),
- Void => Ok("null".to_string()),
- WindowRect(ref x) => json::encode(&x.to_json()),
- }.unwrap();
-
- match self {
- Generic(_) | Cookie(_) | Cookies(_) => obj,
- _ => {
- let mut data = String::with_capacity(11 + obj.len());
- data.push_str("{\"value\": ");
- data.push_str(&*obj);
- data.push_str("}");
- data
- }
- }
+ Wrapper { value: self }.serialize(serializer)
}
}
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, PartialEq)]
pub struct CloseWindowResponse {
pub window_handles: Vec<String>,
}
+impl Serialize for CloseWindowResponse {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.window_handles.serialize(serializer)
+ }
+}
+
impl CloseWindowResponse {
pub fn new(handles: Vec<String>) -> CloseWindowResponse {
CloseWindowResponse { window_handles: handles }
}
}
-impl ToJson for CloseWindowResponse {
- fn to_json(&self) -> Json {
- Json::Array(self.window_handles
- .iter()
- .map(|x| Json::String(x.clone()))
- .collect::<Vec<Json>>())
- }
+#[derive(Clone, Debug, PartialEq)]
+pub struct CookieResponse {
+ pub cookie: Cookie,
}
-#[derive(Debug, RustcEncodable)]
-pub struct NewSessionResponse {
- pub sessionId: String,
- pub capabilities: json::Json
-}
-
-impl NewSessionResponse {
- pub fn new(session_id: String, capabilities: json::Json) -> NewSessionResponse {
- NewSessionResponse {
- capabilities: capabilities,
- sessionId: session_id
- }
+impl Serialize for CookieResponse {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.cookie.serialize(serializer)
}
}
-#[derive(Debug, RustcEncodable)]
-pub struct TimeoutsResponse {
- pub script: u64,
- pub pageLoad: u64,
- pub implicit: u64,
+#[derive(Debug, PartialEq)]
+pub struct CookiesResponse {
+ pub cookies: Vec<Cookie>,
}
-impl TimeoutsResponse {
- pub fn new(script: u64, page_load: u64, implicit: u64) -> TimeoutsResponse {
- TimeoutsResponse {
- script: script,
- pageLoad: page_load,
- implicit: implicit,
- }
+impl Serialize for CookiesResponse {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.cookies.serialize(serializer)
}
}
-#[derive(Debug, RustcEncodable)]
-pub struct ValueResponse {
- pub value: json::Json
-}
-
-impl ValueResponse {
- pub fn new(value: json::Json) -> ValueResponse {
- ValueResponse {
- value: value
- }
- }
-}
-
-#[derive(Debug, RustcEncodable)]
+#[derive(Debug, PartialEq, Serialize)]
pub struct ElementRectResponse {
/// X axis position of the top-left corner of the element relative
// to the current browsing context’s document element in CSS reference
// pixels.
pub x: f64,
/// Y axis position of the top-left corner of the element relative
// to the current browsing context’s document element in CSS reference
@@ -133,17 +100,82 @@ pub struct ElementRectResponse {
/// Width of the element’s [bounding rectangle] in CSS reference
/// pixels.
///
/// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
pub height: f64,
}
-#[derive(Debug)]
+impl ElementRectResponse {
+ pub fn new(x: f64, y: f64, width: f64, height: f64) -> ElementRectResponse {
+ ElementRectResponse {
+ x: x,
+ y: y,
+ width: width,
+ height: height,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Serialize)]
+pub struct NewSessionResponse {
+ pub sessionId: String,
+ pub capabilities: Value
+}
+
+impl NewSessionResponse {
+ pub fn new(session_id: String, capabilities: Value) -> NewSessionResponse {
+ NewSessionResponse {
+ capabilities: capabilities,
+ sessionId: session_id
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Serialize)]
+pub struct TimeoutsResponse {
+ pub script: u64,
+ pub pageLoad: u64,
+ pub implicit: u64,
+}
+
+impl TimeoutsResponse {
+ pub fn new(script: u64, page_load: u64, implicit: u64) -> TimeoutsResponse {
+ TimeoutsResponse {
+ script: script,
+ pageLoad: page_load,
+ implicit: implicit,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq)]
+pub struct ValueResponse {
+ pub value: Value
+}
+
+impl Serialize for ValueResponse {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ self.value.serialize(serializer)
+ }
+}
+
+impl ValueResponse {
+ pub fn new(value: Value) -> ValueResponse {
+ ValueResponse {
+ value: value
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Serialize)]
pub struct WindowRectResponse {
/// `WindowProxy`’s [screenX] attribute.
///
/// [screenX]: https://drafts.csswg.org/cssom-view/#dom-window-screenx
pub x: i32,
/// `WindowProxy`’s [screenY] attribute.
///
@@ -156,175 +188,176 @@ pub struct WindowRectResponse {
pub width: i32,
/// Height of the top-level browsing context’s outer dimensions, including
/// any browser chrome and externally drawn window decorations in CSS
/// reference pixels.
pub height: i32,
}
-impl ToJson for WindowRectResponse {
- fn to_json(&self) -> Json {
- let mut body = BTreeMap::new();
- body.insert("x".to_owned(), self.x.to_json());
- body.insert("y".to_owned(), self.y.to_json());
- body.insert("width".to_owned(), self.width.to_json());
- body.insert("height".to_owned(), self.height.to_json());
- Json::Object(body)
- }
-}
-
-#[derive(Clone, Debug, PartialEq, RustcEncodable)]
-pub struct Cookie {
- pub name: String,
- pub value: String,
- pub path: Nullable<String>,
- pub domain: Nullable<String>,
- pub expiry: Nullable<Date>,
- pub secure: bool,
- pub httpOnly: bool,
-}
-
-impl Into<cookie::Cookie<'static>> for Cookie {
- fn into(self) -> cookie::Cookie<'static> {
- let cookie = cookie::Cookie::build(self.name, self.value)
- .secure(self.secure)
- .http_only(self.httpOnly);
- let cookie = match self.domain {
- Nullable::Value(domain) => cookie.domain(domain),
- Nullable::Null => cookie,
- };
- let cookie = match self.path {
- Nullable::Value(path) => cookie.path(path),
- Nullable::Null => cookie,
- };
- let cookie = match self.expiry {
- Nullable::Value(Date(expiry)) => {
- cookie.expires(time::at(time::Timespec::new(expiry as i64, 0)))
- }
- Nullable::Null => cookie,
- };
- cookie.finish()
- }
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct CookieResponse {
- pub value: Cookie,
-}
-
-#[derive(Debug, RustcEncodable)]
-pub struct CookiesResponse {
- pub value: Vec<Cookie>,
-}
-
#[cfg(test)]
mod tests {
- use super::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse, ElementRectResponse,
- NewSessionResponse, Nullable, TimeoutsResponse, ValueResponse, WebDriverResponse,
- WindowRectResponse};
- use rustc_serialize::json::Json;
- use std::collections::BTreeMap;
+ use common::Date;
+ use serde_json;
+ use super::*;
+ use test::check_serialize;
- fn test(resp: WebDriverResponse, expected_str: &str) {
- let data = resp.to_json_string();
- let actual = Json::from_str(&*data).unwrap();
- let expected = Json::from_str(expected_str).unwrap();
- assert_eq!(actual, expected);
+ #[test]
+ fn test_json_close_window_response() {
+ let json = r#"{"value":["1234"]}"#;
+ let data = WebDriverResponse::CloseWindow(
+ CloseWindowResponse::new(vec!["1234".into()]));
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_close_window() {
- let resp = WebDriverResponse::CloseWindow(
- CloseWindowResponse::new(vec!["test".into()]));
- let expected = r#"{"value": ["test"]}"#;
- test(resp, expected);
+ fn test_json_cookie_response_with_optional() {
+ let json = r#"{"value":{
+ "name":"foo",
+ "value":"bar",
+ "path":"/",
+ "domain":"foo.bar",
+ "secure":true,
+ "httpOnly":false,
+ "expiry":123
+ }}"#;
+ let data = WebDriverResponse::Cookie(CookieResponse { cookie: Cookie {
+ name: "foo".into(),
+ value: "bar".into(),
+ path: Some("/".into()),
+ domain: Some("foo.bar".into()),
+ expiry: Some(Date::new(123)),
+ secure: true,
+ httpOnly: false,
+ }});
+
+ check_serialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_cookie_response_without_optional() {
+ let json = r#"{"value":{
+ "name":"foo",
+ "value":"bar",
+ "path":"/",
+ "domain":null,
+ "secure":true,
+ "httpOnly":false
+ }}"#;
+ let data = WebDriverResponse::Cookie(CookieResponse { cookie: Cookie {
+ name: "foo".into(),
+ value: "bar".into(),
+ path: Some("/".into()),
+ domain: None,
+ expiry: None,
+ secure: true,
+ httpOnly: false,
+ }});
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_cookie() {
- let cookie = Cookie {
- name: "name".into(),
- value: "value".into(),
- path: Nullable::Value("/".into()),
- domain: Nullable::Null,
- expiry: Nullable::Null,
- secure: true,
- httpOnly: false,
- };
- let resp = WebDriverResponse::Cookie(CookieResponse { value: cookie });
- let expected = r#"{"value": {"name": "name", "expiry": null, "value": "value",
-"path": "/", "domain": null, "secure": true, "httpOnly": false}}"#;
- test(resp, expected);
+ fn test_json_cookies_response() {
+ let json = r#"{"value":[{
+ "name":"name",
+ "value":"value",
+ "path":"/",
+ "domain":null,
+ "secure":true,
+ "httpOnly":false
+ }]}"#;
+ let data = WebDriverResponse::Cookies(CookiesResponse {
+ cookies: vec![
+ Cookie {
+ name: "name".into(),
+ value: "value".into(),
+ path: Some("/".into()),
+ domain: None,
+ expiry: None,
+ secure: true,
+ httpOnly: false,
+ }
+ ]
+ });
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_cookies() {
- let resp = WebDriverResponse::Cookies(CookiesResponse {
- value: vec![
- Cookie {
- name: "name".into(),
- value: "value".into(),
- path: Nullable::Value("/".into()),
- domain: Nullable::Null,
- expiry: Nullable::Null,
- secure: true,
- httpOnly: false,
- }
- ]});
- let expected = r#"{"value": [{"name": "name", "value": "value", "path": "/",
-"domain": null, "expiry": null, "secure": true, "httpOnly": false}]}"#;
- test(resp, expected);
+ fn test_json_delete_session_response() {
+ let json = r#"{"value":null}"#;
+ let data = WebDriverResponse::DeleteSession;
+
+ check_serialize(&json, &data);
+ }
+
+ #[test]
+ fn test_json_element_rect_response() {
+ let json = r#"{"value":{"x":0.0,"y":1.0,"width":2.0,"height":3.0}}"#;
+ let data = WebDriverResponse::ElementRect(
+ ElementRectResponse {
+ x: 0f64,
+ y: 1f64,
+ width: 2f64,
+ height: 3f64,
+ });
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_element_rect() {
- let rect = ElementRectResponse {
- x: 0f64,
- y: 1f64,
- width: 2f64,
- height: 3f64,
- };
- let resp = WebDriverResponse::ElementRect(rect);
- let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
- test(resp, expected);
+ fn test_json_generic_value_response() {
+ let json = r#"{"value":{"example":["test"]}}"#;
+ let mut value = serde_json::Map::new();
+ value.insert("example".into(), Value::Array(
+ vec![Value::String("test".into())]));
+
+ let data = WebDriverResponse::Generic(
+ ValueResponse::new(Value::Object(value)));
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_window_rect() {
- let rect = WindowRectResponse {
- x: 0i32,
- y: 1i32,
- width: 2i32,
- height: 3i32,
- };
- let resp = WebDriverResponse::WindowRect(rect);
- let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
- test(resp, expected);
+ fn test_json_new_session_response() {
+ let json = r#"{"value":{"sessionId":"id","capabilities":{}}}"#;
+ let data = WebDriverResponse::NewSession(
+ NewSessionResponse::new(
+ "id".into(),
+ Value::Object(serde_json::Map::new())
+ ));
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_new_session() {
- let resp = WebDriverResponse::NewSession(
- NewSessionResponse::new("test".into(),
- Json::Object(BTreeMap::new())));
- let expected = r#"{"value": {"sessionId": "test", "capabilities": {}}}"#;
- test(resp, expected);
+ fn test_json_timeouts_response() {
+ let json = r#"{"value":{"script":1,"pageLoad":2,"implicit":3}}"#;
+ let data = WebDriverResponse::Timeouts(
+ TimeoutsResponse::new(1, 2, 3));
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_timeouts() {
- let resp = WebDriverResponse::Timeouts(TimeoutsResponse::new(
- 1, 2, 3));
- let expected = r#"{"value": {"script": 1, "pageLoad": 2, "implicit": 3}}"#;
- test(resp, expected);
+ fn test_json_void_response() {
+ let json = r#"{"value":null}"#;
+ let data = WebDriverResponse::Void;
+
+ check_serialize(&json, &data);
}
#[test]
- fn test_value() {
- let mut value = BTreeMap::new();
- value.insert("example".into(), Json::Array(vec![Json::String("test".into())]));
- let resp = WebDriverResponse::Generic(ValueResponse::new(
- Json::Object(value)));
- let expected = r#"{"value": {"example": ["test"]}}"#;
- test(resp, expected);
+ fn test_json_window_rect_response() {
+ let json = r#"{"value":{"x":0,"y":1,"width":2,"height":3}}"#;
+ let data = WebDriverResponse::WindowRect(
+ WindowRectResponse {
+ x: 0i32,
+ y: 1i32,
+ width: 2i32,
+ height: 3i32,
+ });
+
+ check_serialize(&json, &data);
}
}
--- a/testing/webdriver/src/server.rs
+++ b/testing/webdriver/src/server.rs
@@ -1,8 +1,9 @@
+use serde_json;
use std::io::Read;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
@@ -205,27 +206,30 @@ impl<U: WebDriverExtensionRoute> Handler
Err(_) => {
error!("Something terrible happened");
return;
}
}
match recv_res.recv() {
Ok(data) => {
match data {
- Ok(response) => (StatusCode::Ok, response.to_json_string()),
- Err(err) => (err.http_status(), err.to_json_string()),
+ Ok(response) => (StatusCode::Ok,
+ serde_json::to_string_pretty(&response).unwrap()),
+ Err(err) => (err.http_status(),
+ serde_json::to_string_pretty(&err).unwrap()),
}
}
Err(e) => panic!("Error reading response: {:?}", e),
}
}
- Err(err) => (err.http_status(), err.to_json_string()),
+ Err(err) => (err.http_status(),
+ serde_json::to_string_pretty(&err).unwrap()),
};
- debug!("<- {} {}", status, resp_body);
+ debug!("<- {} {:?}", status, resp_body);
{
let resp_status = res.status_mut();
*resp_status = status;
}
res.headers_mut()
.set(ContentType(Mime(TopLevel::Application,
SubLevel::Json,
new file mode 100644
--- /dev/null
+++ b/testing/webdriver/src/test.rs
@@ -0,0 +1,51 @@
+use regex::Regex;
+use serde;
+use serde_json;
+use std;
+
+pub fn check_serialize_deserialize<T>(json: &str, data: &T)
+where
+ T: std::fmt::Debug,
+ T: std::cmp::PartialEq,
+ T: serde::de::DeserializeOwned,
+ T: serde::Serialize,
+{
+ lazy_static! {
+ static ref MIN_REGEX: Regex = Regex::new(r"[\n\t]|\s{4}").unwrap();
+ }
+
+ let min_json = MIN_REGEX.replace_all(json, "");
+
+ assert_eq!(*data, serde_json::from_str::<T>(&min_json).unwrap());
+ assert_eq!(min_json, serde_json::to_string(data).unwrap());
+}
+
+pub fn check_deserialize<T>(json: &str, data: &T)
+where
+ T: std::fmt::Debug,
+ T: std::cmp::PartialEq,
+ T: serde::de::DeserializeOwned,
+{
+ lazy_static! {
+ static ref MIN_REGEX: Regex = Regex::new(r"[\n\t]|\s{4}").unwrap();
+ }
+
+ let min_json = MIN_REGEX.replace_all(json, "");
+
+ assert_eq!(serde_json::from_str::<T>(&min_json).unwrap(), *data);
+}
+
+pub fn check_serialize<T>(json: &str, data: &T)
+where
+ T: std::fmt::Debug,
+ T: std::cmp::PartialEq,
+ T: serde::Serialize,
+{
+ lazy_static! {
+ static ref MIN_REGEX: Regex = Regex::new(r"[\n\t]|\s{4}").unwrap();
+ }
+
+ let min_json = MIN_REGEX.replace_all(json, "");
+
+ assert_eq!(min_json, serde_json::to_string(data).unwrap());
+}