Bug 1529291 - Make WebElement a unit struct. r=ato
authorNupur Baghel <nupurbaghel@gmail.com>
Thu, 07 Mar 2019 11:47:18 +0000
changeset 520739 4655f4514fa4f2a93845edf3582c947d0a39e579
parent 520738 0f2b9b0bf9b966210b3e0dd6709cde35e33558a6
child 520740 e9b5af495b6bf00a4c664bf47ea1e7163128e22f
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersato
bugs1529291
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529291 - Make WebElement a unit struct. r=ato Differential Revision: https://phabricator.services.mozilla.com/D21917
testing/geckodriver/src/command.rs
testing/geckodriver/src/marionette.rs
testing/webdriver/src/actions.rs
testing/webdriver/src/command.rs
testing/webdriver/src/common.rs
--- a/testing/geckodriver/src/command.rs
+++ b/testing/geckodriver/src/command.rs
@@ -82,27 +82,27 @@ impl WebDriverExtensionRoute for GeckoEx
                 GeckoExtensionCommand::SetContext(serde_json::from_value(body_data.clone())?)
             }
             XblAnonymousChildren => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().to_string());
+                let element = WebElement(element_id.as_str().to_string());
                 GeckoExtensionCommand::XblAnonymousChildren(element)
             }
             XblAnonymousByAttribute => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
                 GeckoExtensionCommand::XblAnonymousByAttribute(
-                    WebElement::new(element_id.as_str().into()),
+                    WebElement(element_id.as_str().into()),
                     serde_json::from_value(body_data.clone())?,
                 )
             }
             InstallAddon => {
                 GeckoExtensionCommand::InstallAddon(serde_json::from_value(body_data.clone())?)
             }
             UninstallAddon => {
                 GeckoExtensionCommand::UninstallAddon(serde_json::from_value(body_data.clone())?)
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -402,17 +402,17 @@ impl MarionetteSession {
             ErrorStatus::UnknownError,
             "Failed to extract web element from Marionette response"
         );
         let id = try_opt!(
             value.as_str(),
             ErrorStatus::UnknownError,
             "Failed to convert web element reference value to string"
         ).to_string();
-        Ok(WebElement::new(id))
+        Ok(WebElement(id))
     }
 
     pub fn next_command_id(&mut self) -> u64 {
         self.command_id = self.command_id + 1;
         self.command_id
     }
 
     pub fn response(
@@ -823,17 +823,17 @@ impl MarionetteCommand {
                 );
                 (Some("Marionette:Quit"), Some(Ok(body)))
             }
             DismissAlert => (Some("WebDriver:DismissAlert"), None),
             ElementClear(ref x) => (Some("WebDriver:ElementClear"), Some(x.to_marionette())),
             ElementClick(ref x) => (Some("WebDriver:ElementClick"), Some(x.to_marionette())),
             ElementSendKeys(ref e, ref x) => {
                 let mut data = Map::new();
-                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("id".to_string(), Value::String(e.to_string()));
                 data.insert("text".to_string(), Value::String(x.text.clone()));
                 data.insert(
                     "value".to_string(),
                     serde_json::to_value(
                         x.text
                             .chars()
                             .map(|x| x.to_string())
                             .collect::<Vec<String>>(),
@@ -844,46 +844,46 @@ impl MarionetteCommand {
             ExecuteAsyncScript(ref x) => (
                 Some("WebDriver:ExecuteAsyncScript"),
                 Some(x.to_marionette()),
             ),
             ExecuteScript(ref x) => (Some("WebDriver:ExecuteScript"), Some(x.to_marionette())),
             FindElement(ref x) => (Some("WebDriver:FindElement"), Some(x.to_marionette())),
             FindElementElement(ref e, ref x) => {
                 let mut data = x.to_marionette()?;
-                data.insert("element".to_string(), Value::String(e.id.clone()));
+                data.insert("element".to_string(), Value::String(e.to_string()));
                 (Some("WebDriver:FindElement"), Some(Ok(data)))
             }
             FindElements(ref x) => (Some("WebDriver:FindElements"), Some(x.to_marionette())),
             FindElementElements(ref e, ref x) => {
                 let mut data = x.to_marionette()?;
-                data.insert("element".to_string(), Value::String(e.id.clone()));
+                data.insert("element".to_string(), Value::String(e.to_string()));
                 (Some("WebDriver:FindElements"), Some(Ok(data)))
             }
             FullscreenWindow => (Some("WebDriver:FullscreenWindow"), None),
             Get(ref x) => (Some("WebDriver:Navigate"), Some(x.to_marionette())),
             GetAlertText => (Some("WebDriver:GetAlertText"), None),
             GetActiveElement => (Some("WebDriver:GetActiveElement"), None),
             GetCookies | GetNamedCookie(_) => (Some("WebDriver:GetCookies"), None),
             GetCurrentUrl => (Some("WebDriver:GetCurrentURL"), None),
             GetCSSValue(ref e, ref x) => {
                 let mut data = Map::new();
-                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("id".to_string(), Value::String(e.to_string()));
                 data.insert("propertyName".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementCSSValue"), Some(Ok(data)))
             }
             GetElementAttribute(ref e, ref x) => {
                 let mut data = Map::new();
-                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("id".to_string(), Value::String(e.to_string()));
                 data.insert("name".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementAttribute"), Some(Ok(data)))
             }
             GetElementProperty(ref e, ref x) => {
                 let mut data = Map::new();
-                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("id".to_string(), Value::String(e.to_string()));
                 data.insert("name".to_string(), Value::String(x.clone()));
                 (Some("WebDriver:GetElementProperty"), Some(Ok(data)))
             }
             GetElementRect(ref x) => (Some("WebDriver:GetElementRect"), Some(x.to_marionette())),
             GetElementTagName(ref x) => {
                 (Some("WebDriver:GetElementTagName"), Some(x.to_marionette()))
             }
             GetElementText(ref x) => (Some("WebDriver:GetElementText"), Some(x.to_marionette())),
@@ -933,17 +933,17 @@ impl MarionetteCommand {
             }
             SetTimeouts(ref x) => (Some("WebDriver:SetTimeouts"), Some(x.to_marionette())),
             SetWindowRect(ref x) => (Some("WebDriver:SetWindowRect"), Some(x.to_marionette())),
             SwitchToFrame(ref x) => (Some("WebDriver:SwitchToFrame"), Some(x.to_marionette())),
             SwitchToParentFrame => (Some("WebDriver:SwitchToParentFrame"), None),
             SwitchToWindow(ref x) => (Some("WebDriver:SwitchToWindow"), Some(x.to_marionette())),
             TakeElementScreenshot(ref e) => {
                 let mut data = Map::new();
-                data.insert("id".to_string(), Value::String(e.id.clone()));
+                data.insert("id".to_string(), Value::String(e.to_string()));
                 data.insert("highlights".to_string(), Value::Array(vec![]));
                 data.insert("full".to_string(), Value::Bool(false));
                 (Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
             }
             TakeScreenshot => {
                 let mut data = Map::new();
                 data.insert("id".to_string(), Value::Null);
                 data.insert("highlights".to_string(), Value::Array(vec![]));
@@ -958,24 +958,24 @@ impl MarionetteCommand {
                 SetContext(x) => {
                     (Some("Marionette:SetContext"), Some(x.to_marionette()))
                 }
                 UninstallAddon(x) => {
                     (Some("Addon:Uninstall"), Some(x.to_marionette()))
                 }
                 XblAnonymousByAttribute(e, x) => {
                     let mut data = x.to_marionette()?;
-                    data.insert("element".to_string(), Value::String(e.id.clone()));
+                    data.insert("element".to_string(), Value::String(e.to_string()));
                     (Some("WebDriver:FindElement"), Some(Ok(data)))
                 }
                 XblAnonymousChildren(e) => {
                     let mut data = Map::new();
                     data.insert("using".to_owned(), serde_json::to_value("anon")?);
                     data.insert("value".to_owned(), Value::Null);
-                    data.insert("element".to_string(), serde_json::to_value(e.id.clone())?);
+                    data.insert("element".to_string(), serde_json::to_value(e.to_string())?);
                     (Some("WebDriver:FindElements"), Some(Ok(data)))
                 }
                 TakeFullScreenshot => {
                     let mut data = Map::new();
                     data.insert("id".to_string(), Value::Null);
                     data.insert("highlights".to_string(), Value::Array(vec![]));
                     data.insert("full".to_string(), Value::Bool(true));
                     (Some("WebDriver:TakeScreenshot"), Some(Ok(data)))
@@ -1453,24 +1453,23 @@ impl ToMarionette for NewWindowParameter
         }
         Ok(data)
     }
 }
 
 impl ToMarionette for SwitchToFrameParameters {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         let mut data = Map::new();
-        let key = match self.id {
+        match self.id {
             None => None,
-            Some(FrameId::Short(_)) => Some("id"),
-            Some(FrameId::Element(_)) => Some("element"),
+            Some(FrameId::Short(_)) => data.insert("id".to_string(),
+                                            serde_json::to_value(&self.id)?),
+            Some(FrameId::Element(ref web_element)) => data.insert("element".to_string(),
+                                            serde_json::to_value(web_element.to_string())?),
         };
-        if let Some(x) = key {
-            data.insert(x.to_string(), serde_json::to_value(&self.id)?);
-        }
         Ok(data)
     }
 }
 
 impl ToMarionette for SwitchToWindowParameters {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         let mut data = Map::new();
         data.insert(
@@ -1489,17 +1488,17 @@ impl ToMarionette for TimeoutsParameters
             "Expected an object"
         ).clone())
     }
 }
 
 impl ToMarionette for WebElement {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         let mut data = Map::new();
-        data.insert("id".to_string(), serde_json::to_value(&self.id)?);
+        data.insert("id".to_string(), serde_json::to_value(self.to_string())?);
         Ok(data)
     }
 }
 
 impl ToMarionette for WindowRectParameters {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         Ok(try_opt!(
             serde_json::to_value(self)?.as_object(),
--- a/testing/webdriver/src/actions.rs
+++ b/testing/webdriver/src/actions.rs
@@ -194,17 +194,17 @@ impl Default for PointerOrigin {
 impl<'de> Deserialize<'de> for PointerOrigin {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
         let value = Value::deserialize(deserializer)?;
         if let Some(web_element) = value.get(ELEMENT_KEY) {
             String::deserialize(web_element)
-                .map(|id| PointerOrigin::Element(WebElement { id }))
+                .map(|id| PointerOrigin::Element(WebElement(id)))
                 .map_err(de::Error::custom)
         } else if value == "pointer" {
             Ok(PointerOrigin::Pointer)
         } else if value == "viewport" {
             Ok(PointerOrigin::Viewport)
         } else {
             Err(de::Error::custom(format!(
                 "unknown value `{}`, expected `pointer`, `viewport`, or `element-6066-11e4-a52e-4f735466cecf`",
@@ -213,17 +213,17 @@ impl<'de> Deserialize<'de> for PointerOr
         }
     }
 }
 
 fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
 where
     S: Serializer,
 {
-    element.id.serialize(serializer)
+    element.to_string().serialize(serializer)
 }
 
 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"))
@@ -910,17 +910,17 @@ mod test {
             "origin":{
                 "element-6066-11e4-a52e-4f735466cecf":"elem"
             },
             "x":5,
             "y":10
         }"#;
         let data = PointerAction::Move(PointerMoveAction {
             duration: Some(100),
-            origin: PointerOrigin::Element(WebElement { id: "elem".into() }),
+            origin: PointerOrigin::Element(WebElement("elem".into())),
             x: Some(5),
             y: Some(10),
         });
 
         check_serialize_deserialize(&json, &data);
     }
 
     #[test]
@@ -931,17 +931,17 @@ mod test {
             "origin":{
                 "element-6066-11e4-a52e-4f735466cecf":"elem"
             },
             "x":5,
             "y":10
         }"#;
         let data = PointerAction::Move(PointerMoveAction {
             duration: Some(100),
-            origin: PointerOrigin::Element(WebElement { id: "elem".into() }),
+            origin: PointerOrigin::Element(WebElement("elem".into())),
             x: Some(5),
             y: Some(10),
         });
 
         check_deserialize(&json, &data);
     }
 
     #[test]
@@ -1111,17 +1111,17 @@ mod test {
         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() });
+        let data = PointerOrigin::Element(WebElement("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());
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -144,150 +144,150 @@ impl<U: WebDriverExtensionRoute> WebDriv
             Route::FindElement => WebDriverCommand::FindElement(serde_json::from_str(raw_body)?),
             Route::FindElements => WebDriverCommand::FindElements(serde_json::from_str(raw_body)?),
             Route::FindElementElement => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::FindElementElement(element, serde_json::from_str(raw_body)?)
             }
             Route::FindElementElements => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 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());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::IsDisplayed(element)
             }
             Route::IsSelected => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::IsSelected(element)
             }
             Route::GetElementAttribute => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 let attr = try_opt!(
                     params.name("name"),
                     ErrorStatus::InvalidArgument,
                     "Missing name parameter"
                 ).as_str();
                 WebDriverCommand::GetElementAttribute(element, attr.into())
             }
             Route::GetElementProperty => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 let property = try_opt!(
                     params.name("name"),
                     ErrorStatus::InvalidArgument,
                     "Missing name parameter"
                 ).as_str();
                 WebDriverCommand::GetElementProperty(element, property.into())
             }
             Route::GetCSSValue => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 let property = try_opt!(
                     params.name("propertyName"),
                     ErrorStatus::InvalidArgument,
                     "Missing propertyName parameter"
                 ).as_str();
                 WebDriverCommand::GetCSSValue(element, property.into())
             }
             Route::GetElementText => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::GetElementText(element)
             }
             Route::GetElementTagName => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::GetElementTagName(element)
             }
             Route::GetElementRect => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::GetElementRect(element)
             }
             Route::IsEnabled => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::IsEnabled(element)
             }
             Route::ElementClick => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::ElementClick(element)
             }
             Route::ElementClear => {
                 let element_id = try_opt!(
                     params.name("elementId"),
                     ErrorStatus::InvalidArgument,
                     "Missing elementId parameter"
                 );
-                let element = WebElement::new(element_id.as_str().into());
+                let element = WebElement(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 element = WebElement(element_id.as_str().into());
                 WebDriverCommand::ElementSendKeys(element, serde_json::from_str(raw_body)?)
             }
             Route::ExecuteScript => {
                 WebDriverCommand::ExecuteScript(serde_json::from_str(raw_body)?)
             }
             Route::ExecuteAsyncScript => {
                 WebDriverCommand::ExecuteAsyncScript(serde_json::from_str(raw_body)?)
             }
@@ -324,17 +324,17 @@ impl<U: WebDriverExtensionRoute> WebDriv
             }
             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());
+                let element = WebElement(element_id.as_str().into());
                 WebDriverCommand::TakeElementScreenshot(element)
             }
             Route::Status => WebDriverCommand::Status,
             Route::Extension(ref extension) => extension.command(params, &body_data)?,
         };
         Ok(WebDriverMessage::new(session_id, command))
     }
 
@@ -1192,17 +1192,17 @@ mod tests {
 
         check_deserialize(&json, &data);
     }
 
     #[test]
     fn test_json_take_screenshot_parameters_with_element() {
         let json = r#"{"element":{"element-6066-11e4-a52e-4f735466cecf":"elem"}}"#;
         let data = TakeScreenshotParameters {
-            element: Some(WebElement::new("elem".into())),
+            element: Some(WebElement("elem".into())),
         };
 
         check_deserialize(&json, &data);
     }
 
     #[test]
     fn test_json_take_screenshot_parameters_with_optional_null_field() {
         let json = r#"{"element":null}"#;
@@ -1224,17 +1224,17 @@ mod tests {
         let json = r#"{"element":"foo"}"#;
         assert!(serde_json::from_str::<TakeScreenshotParameters>(&json).is_err());
     }
 
     #[test]
     fn test_json_take_screenshot_parameters_with_unknown_field() {
         let json = r#"{"element":{"element-6066-11e4-a52e-4f735466cecf":"elem"},"foo":"bar"}"#;
         let data = TakeScreenshotParameters {
-            element: Some(WebElement::new("elem".into())),
+            element: Some(WebElement("elem".into())),
         };
 
         check_deserialize(&json, &data);
     }
 
     #[test]
     fn test_json_timeout_parameters_with_values() {
         let json = r#"{"implicit":0,"pageLoad":2.0,"script":9007199254740991}"#;
--- a/testing/webdriver/src/common.rs
+++ b/testing/webdriver/src/common.rs
@@ -1,9 +1,10 @@
 use serde::ser::{Serialize, Serializer};
+use serde::{Deserialize, Deserializer};
 
 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";
 
 pub static MAX_SAFE_INTEGER: u64 = 9_007_199_254_740_991;
 
 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@@ -33,42 +34,64 @@ pub enum FrameId {
     Element(WebElement),
 }
 
 // TODO(Henrik): Remove when ToMarionette trait has been fixed (Bug 1481776)
 fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
 where
     S: Serializer,
 {
-    element.id.serialize(serializer)
+    element.serialize(serializer)
 }
 
 #[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,
 }
 
-#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
-pub struct WebElement {
+#[derive(Clone, Debug, PartialEq)]
+pub struct WebElement(pub String);
+
+// private
+#[derive(Serialize, Deserialize)]
+struct WebElementObject {
     #[serde(rename = "element-6066-11e4-a52e-4f735466cecf")]
-    pub id: String,
+    id: String,
+}
+
+impl Serialize for WebElement {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        WebElementObject { id: self.0.clone() }.serialize(serializer)
+    }
+}
+
+impl<'de> Deserialize<'de> for WebElement {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        Deserialize::deserialize(deserializer).map(|WebElementObject { id }| WebElement(id))
+    }
 }
 
 impl WebElement {
-    pub fn new(id: String) -> WebElement {
-        WebElement { id }
+    pub fn to_string(&self) -> String {
+        self.0.clone()
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::test::{check_serialize, check_serialize_deserialize};
     use serde_json;
@@ -92,18 +115,18 @@ mod tests {
         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()));
+        let json = r#"{"element-6066-11e4-a52e-4f735466cecf":"elem"}"#;
+        let data = FrameId::Element(WebElement("elem".into()));
 
         check_serialize(&json, &data);
     }
 
     #[test]
     fn test_json_frame_id_invalid() {
         let json = r#"true"#;
         assert!(serde_json::from_str::<FrameId>(&json).is_err());
@@ -153,17 +176,17 @@ mod tests {
     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());
+        let data = WebElement("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());