/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::common::{WebElement, ELEMENT_KEY}; use serde::de::{self, Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use serde_json::Value; use std::default::Default; use unicode_segmentation::UnicodeSegmentation; #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct ActionSequence { pub id: String, #[serde(flatten)] pub actions: ActionsType, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] pub enum ActionsType { #[serde(rename = "none")] Null { actions: Vec }, #[serde(rename = "key")] Key { actions: Vec }, #[serde(rename = "pointer")] Pointer { #[serde(default)] parameters: PointerActionParameters, actions: Vec, }, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum NullActionItem { General(GeneralAction), } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] pub enum GeneralAction { #[serde(rename = "pause")] Pause(PauseAction), } #[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct PauseAction { #[serde( default, skip_serializing_if = "Option::is_none", deserialize_with = "deserialize_to_option_u64" )] pub duration: Option, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] pub enum KeyActionItem { General(GeneralAction), Key(KeyAction), } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] pub enum KeyAction { #[serde(rename = "keyDown")] Down(KeyDownAction), #[serde(rename = "keyUp")] Up(KeyUpAction), } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct KeyDownAction { #[serde(deserialize_with = "deserialize_key_action_value")] pub value: String, } #[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 where D: Deserializer<'de>, { String::deserialize(deserializer).map(|value| { // Only a single Unicode grapheme cluster is allowed if value.graphemes(true).count() != 1 { return Err(de::Error::custom(format!( "'{}' should only contain a single Unicode code point", value ))); } Ok(value) })? } #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum PointerType { Mouse, Pen, Touch, } impl Default for PointerType { fn default() -> PointerType { PointerType::Mouse } } #[derive(Debug, Default, PartialEq, Serialize, Deserialize)] pub struct PointerActionParameters { #[serde(rename = "pointerType")] pub pointer_type: PointerType, } #[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] 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), } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct PointerDownAction { pub button: u64, } #[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, #[serde(default)] pub origin: PointerOrigin, #[serde( default, skip_serializing_if = "Option::is_none", deserialize_with = "deserialize_to_option_i64" )] pub x: Option, #[serde( default, skip_serializing_if = "Option::is_none", deserialize_with = "deserialize_to_option_i64" )] pub y: Option, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct PointerUpAction { pub button: u64, } #[derive(Clone, Debug, PartialEq, Serialize)] pub enum PointerOrigin { #[serde( rename = "element-6066-11e4-a52e-4f735466cecf", serialize_with = "serialize_webelement_id" )] Element(WebElement), #[serde(rename = "pointer")] Pointer, #[serde(rename = "viewport")] Viewport, } impl Default for PointerOrigin { fn default() -> PointerOrigin { PointerOrigin::Viewport } } // TODO: The custom deserializer can be removed once the support of the legacy // ELEMENT key has been removed from Selenium bindings // See: https://github.com/SeleniumHQ/selenium/issues/6393 impl<'de> Deserialize<'de> for PointerOrigin { fn deserialize(deserializer: D) -> Result 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_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`", value.to_string() ))) } } } fn serialize_webelement_id(element: &WebElement, serializer: S) -> Result where S: Serializer, { element.to_string().serialize(serializer) } fn deserialize_to_option_i64<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Option::deserialize(deserializer)? .ok_or_else(|| de::Error::custom("invalid type: null, expected i64")) } fn deserialize_to_option_u64<'de, D>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, { Option::deserialize(deserializer)? .ok_or_else(|| de::Error::custom("invalid type: null, expected i64")) } #[cfg(test)] mod test { use super::*; use crate::test::{assert_de, assert_ser_de}; use serde_json::{self, json}; #[test] fn test_json_action_sequence_null() { let json = json!({ "id": "some_key", "type": "none", "actions": [{ "type": "pause", "duration": 1, }] }); let seq = ActionSequence { id: "some_key".into(), actions: ActionsType::Null { actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1), }))], }, }; assert_ser_de(&seq, json); } #[test] fn test_json_action_sequence_key() { let json = json!({ "id": "some_key", "type": "key", "actions": [ {"type": "keyDown", "value": "f"}, ], }); let seq = ActionSequence { id: "some_key".into(), actions: ActionsType::Key { actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction { value: String::from("f"), }))], }, }; assert_ser_de(&seq, json); } #[test] fn test_json_action_sequence_pointer() { let json = json!({ "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 seq = ActionSequence { id: "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 })), ], }, }; assert_ser_de(&seq, json); } #[test] fn test_json_action_sequence_id_missing() { let json = json!({ "type": "key", "actions": [], }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_action_sequence_id_null() { let json = json!({ "id": null, "type": "key", "actions": [], }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_action_sequence_actions_missing() { assert!(serde_json::from_value::(json!({"id": "3"})).is_err()); } #[test] fn test_json_action_sequence_actions_null() { let json = json!({ "id": "3", "actions": null, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_action_sequence_actions_invalid_type() { let json = json!({ "id": "3", "actions": "foo", }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_actions_type_null() { let json = json!({ "type": "none", "actions": [{ "type": "pause", "duration": 1, }], }); let null = ActionsType::Null { actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1), }))], }; assert_ser_de(&null, json); } #[test] fn test_json_actions_type_key() { let json = json!({ "type": "key", "actions": [{ "type": "keyDown", "value": "f", }], }); let key = ActionsType::Key { actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction { value: String::from("f"), }))], }; assert_ser_de(&key, json); } #[test] fn test_json_actions_type_pointer() { let json = json!({ "type": "pointer", "parameters": {"pointerType": "mouse"}, "actions": [ {"type": "pointerDown", "button": 1}, ]}); let pointer = ActionsType::Pointer { parameters: PointerActionParameters { pointer_type: PointerType::Mouse, }, actions: vec![PointerActionItem::Pointer(PointerAction::Down( PointerDownAction { button: 1 }, ))], }; assert_ser_de(&pointer, json); } #[test] fn test_json_actions_type_pointer_with_parameters_missing() { let json = json!({ "type": "pointer", "actions": [ {"type": "pointerDown", "button": 1}, ]}); let pointer = ActionsType::Pointer { parameters: PointerActionParameters { pointer_type: PointerType::Mouse, }, actions: vec![PointerActionItem::Pointer(PointerAction::Down( PointerDownAction { button: 1 }, ))], }; assert_de(&pointer, json); } #[test] fn test_json_actions_type_pointer_with_parameters_invalid_type() { let json = json!({ "type": "pointer", "parameters": null, "actions": [ {"type":"pointerDown", "button": 1}, ]}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_actions_type_invalid() { let json = json!({"actions": [{"foo": "bar"}]}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_null_action_item_general() { let pause = NullActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) })); assert_ser_de(&pause, json!({"type": "pause", "duration": 1})); } #[test] fn test_json_null_action_item_invalid_type() { assert!(serde_json::from_value::(json!({"type": "invalid"})).is_err()); } #[test] fn test_json_general_action_pause() { let pause = GeneralAction::Pause(PauseAction { duration: Some(1) }); assert_ser_de(&pause, json!({"type": "pause", "duration": 1})); } #[test] fn test_json_general_action_pause_with_duration_missing() { let pause = GeneralAction::Pause(PauseAction { duration: None }); assert_ser_de(&pause, json!({"type": "pause"})); } #[test] fn test_json_general_action_pause_with_duration_null() { let json = json!({"type": "pause", "duration": null}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_general_action_pause_with_duration_invalid_type() { let json = json!({"type": "pause", "duration":" foo"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_general_action_pause_with_duration_negative() { let json = json!({"type": "pause", "duration": -30}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_item_general() { let pause = KeyActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) })); assert_ser_de(&pause, json!({"type": "pause", "duration": 1})); } #[test] fn test_json_key_action_item_key() { let key_down = KeyActionItem::Key(KeyAction::Down(KeyDownAction { value: String::from("f"), })); assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"})); } #[test] fn test_json_key_action_item_invalid_type() { assert!(serde_json::from_value::(json!({"type": "invalid"})).is_err()); } #[test] fn test_json_key_action_missing_subtype() { assert!(serde_json::from_value::(json!({"value": "f"})).is_err()); } #[test] fn test_json_key_action_wrong_subtype() { let json = json!({"type": "pause", "value": "f"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_down() { let key_down = KeyAction::Down(KeyDownAction { value: "f".to_string(), }); assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"})); } #[test] fn test_json_key_action_down_with_value_unicode() { let key_down = KeyAction::Down(KeyDownAction { value: "à".to_string(), }); assert_ser_de(&key_down, json!({"type": "keyDown", "value": "à"})); } #[test] fn test_json_key_action_down_with_value_unicode_encoded() { let key_down = KeyAction::Down(KeyDownAction { value: "à".to_string(), }); assert_de(&key_down, json!({"type": "keyDown", "value": "\u{00E0}"})); } #[test] fn test_json_key_action_down_with_value_missing() { assert!(serde_json::from_value::(json!({"type": "keyDown"})).is_err()); } #[test] fn test_json_key_action_down_with_value_null() { let json = json!({"type": "keyDown", "value": null}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_down_with_value_invalid_type() { let json = json!({"type": "keyDown", "value": ["f", "o", "o"]}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_down_with_multiple_code_points() { let json = json!({"type": "keyDown", "value": "fo"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_up() { let key_up = KeyAction::Up(KeyUpAction { value: "f".to_string(), }); assert_ser_de(&key_up, json!({"type": "keyUp", "value": "f"})); } #[test] fn test_json_key_action_up_with_value_unicode() { let key_up = KeyAction::Up(KeyUpAction { value: "à".to_string(), }); assert_ser_de(&key_up, json!({"type":"keyUp", "value": "à"})); } #[test] fn test_json_key_action_up_with_value_unicode_encoded() { let key_up = KeyAction::Up(KeyUpAction { value: "à".to_string(), }); assert_de(&key_up, json!({"type": "keyUp", "value": "\u{00E0}"})); } #[test] fn test_json_key_action_up_with_value_missing() { assert!(serde_json::from_value::(json!({"type": "keyUp"})).is_err()); } #[test] fn test_json_key_action_up_with_value_null() { let json = json!({"type": "keyUp", "value": null}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_up_with_value_invalid_type() { let json = json!({"type": "keyUp", "value": ["f","o","o"]}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_key_action_up_with_multiple_code_points() { let json = json!({"type": "keyUp", "value": "fo"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_item_general() { let pause = PointerActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) })); assert_ser_de(&pause, json!({"type": "pause", "duration": 1})); } #[test] fn test_json_pointer_action_item_pointer() { let cancel = PointerActionItem::Pointer(PointerAction::Cancel); assert_ser_de(&cancel, json!({"type": "pointerCancel"})); } #[test] fn test_json_pointer_action_item_invalid() { assert!(serde_json::from_value::(json!({"type": "invalid"})).is_err()); } #[test] fn test_json_pointer_action_parameters_mouse() { let mouse = PointerActionParameters { pointer_type: PointerType::Mouse, }; assert_ser_de(&mouse, json!({"pointerType": "mouse"})); } #[test] fn test_json_pointer_action_parameters_pen() { let pen = PointerActionParameters { pointer_type: PointerType::Pen, }; assert_ser_de(&pen, json!({"pointerType": "pen"})); } #[test] fn test_json_pointer_action_parameters_touch() { let touch = PointerActionParameters { pointer_type: PointerType::Touch, }; assert_ser_de(&touch, json!({"pointerType": "touch"})); } #[test] fn test_json_pointer_action_item_invalid_type() { let json = json!({"type": "pointerInvalid"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_missing_subtype() { assert!(serde_json::from_value::(json!({"button": 1})).is_err()); } #[test] fn test_json_pointer_action_invalid_subtype() { let json = json!({"type": "invalid", "button": 1}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_cancel() { assert_ser_de(&PointerAction::Cancel, json!({"type": "pointerCancel"})); } #[test] fn test_json_pointer_action_down() { let pointer_down = PointerAction::Down(PointerDownAction { button: 1 }); assert_ser_de(&pointer_down, json!({"type": "pointerDown", "button": 1})); } #[test] fn test_json_pointer_action_down_with_button_missing() { let json = json!({"type": "pointerDown"}); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_down_with_button_null() { let json = json!({ "type": "pointerDown", "button": null, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_down_with_button_invalid_type() { let json = json!({ "type": "pointerDown", "button": "foo", }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_down_with_button_negative() { let json = json!({ "type": "pointerDown", "button": -30, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": 5, "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Viewport, x: Some(5), y: Some(10), }); assert_ser_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_missing_subtype() { let json = json!({ "duration": 100, "origin": "viewport", "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_wrong_subtype() { let json = json!({ "type": "pointerUp", "duration": 100, "origin": "viewport", "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_duration_missing() { let json = json!({ "type": "pointerMove", "origin": "viewport", "x": 5, "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: None, origin: PointerOrigin::Viewport, x: Some(5), y: Some(10), }); assert_ser_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_duration_null() { let json = json!({ "type": "pointerMove", "duration": null, "origin": "viewport", "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_duration_invalid_type() { let json = json!({ "type": "pointerMove", "duration": "invalid", "origin": "viewport", "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_duration_negative() { let json = json!({ "type": "pointerMove", "duration": -30, "origin": "viewport", "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_origin_missing() { let json = json!({ "type": "pointerMove", "duration": 100, "x": 5, "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Viewport, x: Some(5), y: Some(10), }); assert_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_origin_webelement() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": {ELEMENT_KEY: "elem"}, "x": 5, "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Element(WebElement("elem".into())), x: Some(5), y: Some(10), }); assert_ser_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_origin_webelement_and_legacy_element() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": {ELEMENT_KEY: "elem"}, "x": 5, "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Element(WebElement("elem".into())), x: Some(5), y: Some(10), }); assert_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_origin_only_legacy_element() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": {ELEMENT_KEY: "elem"}, "x": 5, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_x_missing() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "y": 10, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Viewport, x: None, y: Some(10), }); assert_ser_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_x_null() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": null, "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_x_invalid_type() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": "invalid", "y": 10, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_y_missing() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": 5, }); let pointer_move = PointerAction::Move(PointerMoveAction { duration: Some(100), origin: PointerOrigin::Viewport, x: Some(5), y: None, }); assert_ser_de(&pointer_move, json); } #[test] fn test_json_pointer_action_move_with_y_null() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": 5, "y": null, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_move_with_y_invalid_type() { let json = json!({ "type": "pointerMove", "duration": 100, "origin": "viewport", "x": 5, "y": "invalid", }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_up() { let pointer_up = PointerAction::Up(PointerUpAction { button: 1 }); assert_ser_de(&pointer_up, json!({"type": "pointerUp", "button": 1})); } #[test] fn test_json_pointer_action_up_with_button_missing() { assert!(serde_json::from_value::(json!({"type": "pointerUp"})).is_err()); } #[test] fn test_json_pointer_action_up_with_button_null() { let json = json!({ "type": "pointerUp", "button": null, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_up_with_button_invalid_type() { let json = json!({ "type": "pointerUp", "button": "foo", }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_action_up_with_button_negative() { let json = json!({ "type": "pointerUp", "button": -30, }); assert!(serde_json::from_value::(json).is_err()); } #[test] fn test_json_pointer_origin_pointer() { assert_ser_de(&PointerOrigin::Pointer, json!("pointer")); } #[test] fn test_json_pointer_origin_viewport() { assert_ser_de(&PointerOrigin::Viewport, json!("viewport")); } #[test] fn test_json_pointer_origin_web_element() { let element = PointerOrigin::Element(WebElement("elem".into())); assert_ser_de(&element, json!({ELEMENT_KEY: "elem"})); } #[test] fn test_json_pointer_origin_invalid_type() { assert!(serde_json::from_value::(json!("invalid")).is_err()); } #[test] fn test_json_pointer_type_mouse() { assert_ser_de(&PointerType::Mouse, json!("mouse")); } #[test] fn test_json_pointer_type_pen() { assert_ser_de(&PointerType::Pen, json!("pen")); } #[test] fn test_json_pointer_type_touch() { assert_ser_de(&PointerType::Touch, json!("touch")); } #[test] fn test_json_pointer_type_invalid_type() { assert!(serde_json::from_value::(json!("invalid")).is_err()); } }