webdriver/
actions.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::common::{WebElement, ELEMENT_KEY};
6use icu_segmenter::GraphemeClusterSegmenter;
7use serde::de::{self, Deserialize, Deserializer};
8use serde::ser::{Serialize, Serializer};
9use serde_json::Value;
10use std::default::Default;
11use std::f64;
12
13#[derive(Debug, PartialEq, Serialize, Deserialize)]
14pub struct ActionSequence {
15    pub id: String,
16    #[serde(flatten)]
17    pub actions: ActionsType,
18}
19
20#[derive(Debug, PartialEq, Serialize, Deserialize)]
21#[serde(tag = "type")]
22pub enum ActionsType {
23    #[serde(rename = "none")]
24    Null { actions: Vec<NullActionItem> },
25    #[serde(rename = "key")]
26    Key { actions: Vec<KeyActionItem> },
27    #[serde(rename = "pointer")]
28    Pointer {
29        #[serde(default)]
30        parameters: PointerActionParameters,
31        actions: Vec<PointerActionItem>,
32    },
33    #[serde(rename = "wheel")]
34    Wheel { actions: Vec<WheelActionItem> },
35}
36
37#[derive(Debug, PartialEq, Serialize, Deserialize)]
38#[serde(untagged)]
39pub enum NullActionItem {
40    General(GeneralAction),
41}
42
43#[derive(Debug, PartialEq, Serialize, Deserialize)]
44#[serde(tag = "type")]
45pub enum GeneralAction {
46    #[serde(rename = "pause")]
47    Pause(PauseAction),
48}
49
50#[derive(Debug, PartialEq, Serialize, Deserialize)]
51pub struct PauseAction {
52    #[serde(
53        default,
54        skip_serializing_if = "Option::is_none",
55        deserialize_with = "deserialize_to_option_u64"
56    )]
57    pub duration: Option<u64>,
58}
59
60#[derive(Debug, PartialEq, Serialize, Deserialize)]
61#[serde(untagged)]
62pub enum KeyActionItem {
63    General(GeneralAction),
64    Key(KeyAction),
65}
66
67#[derive(Debug, PartialEq, Serialize, Deserialize)]
68#[serde(tag = "type")]
69pub enum KeyAction {
70    #[serde(rename = "keyDown")]
71    Down(KeyDownAction),
72    #[serde(rename = "keyUp")]
73    Up(KeyUpAction),
74}
75
76#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
77pub struct KeyDownAction {
78    #[serde(deserialize_with = "deserialize_key_action_value")]
79    pub value: String,
80}
81
82#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
83pub struct KeyUpAction {
84    #[serde(deserialize_with = "deserialize_key_action_value")]
85    pub value: String,
86}
87
88fn deserialize_key_action_value<'de, D>(deserializer: D) -> Result<String, D::Error>
89where
90    D: Deserializer<'de>,
91{
92    String::deserialize(deserializer).map(|value| {
93        // Only a single Unicode grapheme cluster is allowed
94        if GraphemeClusterSegmenter::new().segment_str(&value).count() != 2 {
95            return Err(de::Error::custom(format!(
96                "'{}' should only contain a single Unicode code point",
97                value
98            )));
99        }
100
101        Ok(value)
102    })?
103}
104
105#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
106#[serde(rename_all = "lowercase")]
107pub enum PointerType {
108    #[default]
109    Mouse,
110    Pen,
111    Touch,
112}
113
114#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
115pub struct PointerActionParameters {
116    #[serde(rename = "pointerType")]
117    pub pointer_type: PointerType,
118}
119
120#[derive(Debug, PartialEq, Serialize, Deserialize)]
121#[serde(untagged)]
122pub enum PointerActionItem {
123    General(GeneralAction),
124    Pointer(PointerAction),
125}
126
127#[derive(Debug, PartialEq, Serialize, Deserialize)]
128#[serde(tag = "type")]
129pub enum PointerAction {
130    #[serde(rename = "pointerCancel")]
131    Cancel,
132    #[serde(rename = "pointerDown")]
133    Down(PointerDownAction),
134    #[serde(rename = "pointerMove")]
135    Move(PointerMoveAction),
136    #[serde(rename = "pointerUp")]
137    Up(PointerUpAction),
138}
139
140#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
141pub struct PointerDownAction {
142    pub button: u64,
143    #[serde(
144        default,
145        skip_serializing_if = "Option::is_none",
146        deserialize_with = "deserialize_to_option_u64"
147    )]
148    pub width: Option<u64>,
149    #[serde(
150        default,
151        skip_serializing_if = "Option::is_none",
152        deserialize_with = "deserialize_to_option_u64"
153    )]
154    pub height: Option<u64>,
155    #[serde(
156        default,
157        skip_serializing_if = "Option::is_none",
158        deserialize_with = "deserialize_to_pressure"
159    )]
160    pub pressure: Option<f64>,
161    #[serde(
162        default,
163        skip_serializing_if = "Option::is_none",
164        deserialize_with = "deserialize_to_tangential_pressure"
165    )]
166    pub tangentialPressure: Option<f64>,
167    #[serde(
168        default,
169        skip_serializing_if = "Option::is_none",
170        deserialize_with = "deserialize_to_tilt"
171    )]
172    pub tiltX: Option<i64>,
173    #[serde(
174        default,
175        skip_serializing_if = "Option::is_none",
176        deserialize_with = "deserialize_to_tilt"
177    )]
178    pub tiltY: Option<i64>,
179    #[serde(
180        default,
181        skip_serializing_if = "Option::is_none",
182        deserialize_with = "deserialize_to_twist"
183    )]
184    pub twist: Option<u64>,
185    #[serde(
186        default,
187        skip_serializing_if = "Option::is_none",
188        deserialize_with = "deserialize_to_altitude_angle"
189    )]
190    pub altitudeAngle: Option<f64>,
191    #[serde(
192        default,
193        skip_serializing_if = "Option::is_none",
194        deserialize_with = "deserialize_to_azimuth_angle"
195    )]
196    pub azimuthAngle: Option<f64>,
197}
198
199#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
200pub struct PointerMoveAction {
201    #[serde(
202        default,
203        skip_serializing_if = "Option::is_none",
204        deserialize_with = "deserialize_to_option_u64"
205    )]
206    pub duration: Option<u64>,
207    #[serde(default)]
208    pub origin: PointerOrigin,
209    pub x: f64,
210    pub y: f64,
211    #[serde(
212        default,
213        skip_serializing_if = "Option::is_none",
214        deserialize_with = "deserialize_to_option_u64"
215    )]
216    pub width: Option<u64>,
217    #[serde(
218        default,
219        skip_serializing_if = "Option::is_none",
220        deserialize_with = "deserialize_to_option_u64"
221    )]
222    pub height: Option<u64>,
223    #[serde(
224        default,
225        skip_serializing_if = "Option::is_none",
226        deserialize_with = "deserialize_to_pressure"
227    )]
228    pub pressure: Option<f64>,
229    #[serde(
230        default,
231        skip_serializing_if = "Option::is_none",
232        deserialize_with = "deserialize_to_tangential_pressure"
233    )]
234    pub tangentialPressure: Option<f64>,
235    #[serde(
236        default,
237        skip_serializing_if = "Option::is_none",
238        deserialize_with = "deserialize_to_tilt"
239    )]
240    pub tiltX: Option<i64>,
241    #[serde(
242        default,
243        skip_serializing_if = "Option::is_none",
244        deserialize_with = "deserialize_to_tilt"
245    )]
246    pub tiltY: Option<i64>,
247    #[serde(
248        default,
249        skip_serializing_if = "Option::is_none",
250        deserialize_with = "deserialize_to_twist"
251    )]
252    pub twist: Option<u64>,
253    #[serde(
254        default,
255        skip_serializing_if = "Option::is_none",
256        deserialize_with = "deserialize_to_altitude_angle"
257    )]
258    pub altitudeAngle: Option<f64>,
259    #[serde(
260        default,
261        skip_serializing_if = "Option::is_none",
262        deserialize_with = "deserialize_to_azimuth_angle"
263    )]
264    pub azimuthAngle: Option<f64>,
265}
266
267#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
268pub struct PointerUpAction {
269    pub button: u64,
270    #[serde(
271        default,
272        skip_serializing_if = "Option::is_none",
273        deserialize_with = "deserialize_to_option_u64"
274    )]
275    pub width: Option<u64>,
276    #[serde(
277        default,
278        skip_serializing_if = "Option::is_none",
279        deserialize_with = "deserialize_to_option_u64"
280    )]
281    pub height: Option<u64>,
282    #[serde(
283        default,
284        skip_serializing_if = "Option::is_none",
285        deserialize_with = "deserialize_to_pressure"
286    )]
287    pub pressure: Option<f64>,
288    #[serde(
289        default,
290        skip_serializing_if = "Option::is_none",
291        deserialize_with = "deserialize_to_tangential_pressure"
292    )]
293    pub tangentialPressure: Option<f64>,
294    #[serde(
295        default,
296        skip_serializing_if = "Option::is_none",
297        deserialize_with = "deserialize_to_tilt"
298    )]
299    pub tiltX: Option<i64>,
300    #[serde(
301        default,
302        skip_serializing_if = "Option::is_none",
303        deserialize_with = "deserialize_to_tilt"
304    )]
305    pub tiltY: Option<i64>,
306    #[serde(
307        default,
308        skip_serializing_if = "Option::is_none",
309        deserialize_with = "deserialize_to_twist"
310    )]
311    pub twist: Option<u64>,
312    #[serde(
313        default,
314        skip_serializing_if = "Option::is_none",
315        deserialize_with = "deserialize_to_altitude_angle"
316    )]
317    pub altitudeAngle: Option<f64>,
318    #[serde(
319        default,
320        skip_serializing_if = "Option::is_none",
321        deserialize_with = "deserialize_to_azimuth_angle"
322    )]
323    pub azimuthAngle: Option<f64>,
324}
325
326#[derive(Clone, Debug, Default, PartialEq, Serialize)]
327pub enum PointerOrigin {
328    #[serde(
329        rename = "element-6066-11e4-a52e-4f735466cecf",
330        serialize_with = "serialize_webelement_id"
331    )]
332    Element(WebElement),
333    #[serde(rename = "pointer")]
334    Pointer,
335    #[serde(rename = "viewport")]
336    #[default]
337    Viewport,
338}
339
340// TODO: The custom deserializer can be removed once the support of the legacy
341// ELEMENT key has been removed from Selenium bindings
342// See: https://github.com/SeleniumHQ/selenium/issues/6393
343impl<'de> Deserialize<'de> for PointerOrigin {
344    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
345    where
346        D: Deserializer<'de>,
347    {
348        let value = Value::deserialize(deserializer)?;
349        if let Some(web_element) = value.get(ELEMENT_KEY) {
350            String::deserialize(web_element)
351                .map(|id| PointerOrigin::Element(WebElement(id)))
352                .map_err(de::Error::custom)
353        } else if value == "pointer" {
354            Ok(PointerOrigin::Pointer)
355        } else if value == "viewport" {
356            Ok(PointerOrigin::Viewport)
357        } else {
358            Err(de::Error::custom(format!(
359                "unknown value `{}`, expected `pointer`, `viewport`, or `element-6066-11e4-a52e-4f735466cecf`",
360                value
361            )))
362        }
363    }
364}
365
366#[derive(Debug, PartialEq, Serialize, Deserialize)]
367#[serde(untagged)]
368pub enum WheelActionItem {
369    General(GeneralAction),
370    Wheel(WheelAction),
371}
372
373#[derive(Debug, PartialEq, Serialize, Deserialize)]
374#[serde(tag = "type")]
375pub enum WheelAction {
376    #[serde(rename = "scroll")]
377    Scroll(WheelScrollAction),
378}
379
380#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
381pub struct WheelScrollAction {
382    #[serde(
383        default,
384        skip_serializing_if = "Option::is_none",
385        deserialize_with = "deserialize_to_option_u64"
386    )]
387    pub duration: Option<u64>,
388    #[serde(default)]
389    pub origin: PointerOrigin,
390    pub x: Option<i64>,
391    pub y: Option<i64>,
392    pub deltaX: Option<i64>,
393    pub deltaY: Option<i64>,
394}
395
396fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
397where
398    S: Serializer,
399{
400    element.to_string().serialize(serializer)
401}
402
403fn deserialize_to_option_i64<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
404where
405    D: Deserializer<'de>,
406{
407    Option::deserialize(deserializer)?
408        .ok_or_else(|| de::Error::custom("invalid type: null, expected i64"))
409}
410
411fn deserialize_to_option_u64<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
412where
413    D: Deserializer<'de>,
414{
415    Option::deserialize(deserializer)?
416        .ok_or_else(|| de::Error::custom("invalid type: null, expected i64"))
417}
418
419fn deserialize_to_option_f64<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
420where
421    D: Deserializer<'de>,
422{
423    Option::deserialize(deserializer)?
424        .ok_or_else(|| de::Error::custom("invalid type: null, expected f64"))
425}
426
427fn deserialize_to_pressure<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
428where
429    D: Deserializer<'de>,
430{
431    let opt_value = deserialize_to_option_f64(deserializer)?;
432    if let Some(value) = opt_value {
433        if !(0f64..=1.0).contains(&value) {
434            return Err(de::Error::custom(format!("{} is outside range 0-1", value)));
435        }
436    };
437    Ok(opt_value)
438}
439
440fn deserialize_to_tangential_pressure<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
441where
442    D: Deserializer<'de>,
443{
444    let opt_value = deserialize_to_option_f64(deserializer)?;
445    if let Some(value) = opt_value {
446        if !(-1.0..=1.0).contains(&value) {
447            return Err(de::Error::custom(format!(
448                "{} is outside range -1-1",
449                value
450            )));
451        }
452    };
453    Ok(opt_value)
454}
455
456fn deserialize_to_tilt<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
457where
458    D: Deserializer<'de>,
459{
460    let opt_value = deserialize_to_option_i64(deserializer)?;
461    if let Some(value) = opt_value {
462        if !(-90..=90).contains(&value) {
463            return Err(de::Error::custom(format!(
464                "{} is outside range -90-90",
465                value
466            )));
467        }
468    };
469    Ok(opt_value)
470}
471
472fn deserialize_to_twist<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
473where
474    D: Deserializer<'de>,
475{
476    let opt_value = deserialize_to_option_u64(deserializer)?;
477    if let Some(value) = opt_value {
478        if !(0..=359).contains(&value) {
479            return Err(de::Error::custom(format!(
480                "{} is outside range 0-359",
481                value
482            )));
483        }
484    };
485    Ok(opt_value)
486}
487
488fn deserialize_to_altitude_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
489where
490    D: Deserializer<'de>,
491{
492    let opt_value = deserialize_to_option_f64(deserializer)?;
493    if let Some(value) = opt_value {
494        if !(0f64..=f64::consts::FRAC_PI_2).contains(&value) {
495            return Err(de::Error::custom(format!(
496                "{} is outside range 0-PI/2",
497                value
498            )));
499        }
500    };
501    Ok(opt_value)
502}
503
504fn deserialize_to_azimuth_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
505where
506    D: Deserializer<'de>,
507{
508    let opt_value = deserialize_to_option_f64(deserializer)?;
509    if let Some(value) = opt_value {
510        if !(0f64..=f64::consts::TAU).contains(&value) {
511            return Err(de::Error::custom(format!(
512                "{} is outside range 0-2*PI",
513                value
514            )));
515        }
516    };
517    Ok(opt_value)
518}
519
520#[cfg(test)]
521mod test {
522    use super::*;
523    use crate::test::{assert_de, assert_ser_de};
524    use serde_json::{self, json, Value};
525
526    #[test]
527    fn test_json_action_sequence_null() {
528        let json = json!({
529            "id": "some_key",
530            "type": "none",
531            "actions": [{
532                "type": "pause",
533                "duration": 1,
534            }]
535        });
536        let seq = ActionSequence {
537            id: "some_key".into(),
538            actions: ActionsType::Null {
539                actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction {
540                    duration: Some(1),
541                }))],
542            },
543        };
544
545        assert_ser_de(&seq, json);
546    }
547
548    #[test]
549    fn test_json_action_sequence_key() {
550        let json = json!({
551            "id": "some_key",
552            "type": "key",
553            "actions": [
554                {"type": "keyDown", "value": "f"},
555            ],
556        });
557        let seq = ActionSequence {
558            id: "some_key".into(),
559            actions: ActionsType::Key {
560                actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction {
561                    value: String::from("f"),
562                }))],
563            },
564        };
565
566        assert_ser_de(&seq, json);
567    }
568
569    #[test]
570    fn test_json_action_sequence_pointer() {
571        let json = json!({
572            "id": "some_pointer",
573            "type": "pointer",
574            "parameters": {
575                "pointerType": "mouse"
576            },
577            "actions": [
578                {"type": "pointerDown", "button": 0},
579                {"type": "pointerMove", "origin": "pointer", "x": 10.5, "y": 20.5},
580                {"type": "pointerUp", "button": 0},
581            ]
582        });
583        let seq = ActionSequence {
584            id: "some_pointer".into(),
585            actions: ActionsType::Pointer {
586                parameters: PointerActionParameters {
587                    pointer_type: PointerType::Mouse,
588                },
589                actions: vec![
590                    PointerActionItem::Pointer(PointerAction::Down(PointerDownAction {
591                        button: 0,
592                        ..Default::default()
593                    })),
594                    PointerActionItem::Pointer(PointerAction::Move(PointerMoveAction {
595                        origin: PointerOrigin::Pointer,
596                        duration: None,
597                        x: 10.5,
598                        y: 20.5,
599                        ..Default::default()
600                    })),
601                    PointerActionItem::Pointer(PointerAction::Up(PointerUpAction {
602                        button: 0,
603                        ..Default::default()
604                    })),
605                ],
606            },
607        };
608
609        assert_ser_de(&seq, json);
610    }
611
612    #[test]
613    fn test_json_action_sequence_id_missing() {
614        let json = json!({
615            "type": "key",
616            "actions": [],
617        });
618        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
619    }
620
621    #[test]
622    fn test_json_action_sequence_id_null() {
623        let json = json!({
624            "id": null,
625            "type": "key",
626            "actions": [],
627        });
628        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
629    }
630
631    #[test]
632    fn test_json_action_sequence_actions_missing() {
633        assert!(serde_json::from_value::<ActionSequence>(json!({"id": "3"})).is_err());
634    }
635
636    #[test]
637    fn test_json_action_sequence_actions_null() {
638        let json = json!({
639            "id": "3",
640            "actions": null,
641        });
642        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
643    }
644
645    #[test]
646    fn test_json_action_sequence_actions_invalid_type() {
647        let json = json!({
648            "id": "3",
649            "actions": "foo",
650        });
651        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
652    }
653
654    #[test]
655    fn test_json_actions_type_null() {
656        let json = json!({
657            "type": "none",
658            "actions": [{
659                "type": "pause",
660                "duration": 1,
661            }],
662        });
663        let null = ActionsType::Null {
664            actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction {
665                duration: Some(1),
666            }))],
667        };
668
669        assert_ser_de(&null, json);
670    }
671
672    #[test]
673    fn test_json_actions_type_key() {
674        let json = json!({
675            "type": "key",
676            "actions": [{
677                "type": "keyDown",
678                "value": "f",
679            }],
680        });
681        let key = ActionsType::Key {
682            actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction {
683                value: String::from("f"),
684            }))],
685        };
686
687        assert_ser_de(&key, json);
688    }
689
690    #[test]
691    fn test_json_actions_type_pointer() {
692        let json = json!({
693        "type": "pointer",
694        "parameters": {"pointerType": "mouse"},
695        "actions": [
696            {"type": "pointerDown", "button": 1},
697        ]});
698        let pointer = ActionsType::Pointer {
699            parameters: PointerActionParameters {
700                pointer_type: PointerType::Mouse,
701            },
702            actions: vec![PointerActionItem::Pointer(PointerAction::Down(
703                PointerDownAction {
704                    button: 1,
705                    ..Default::default()
706                },
707            ))],
708        };
709
710        assert_ser_de(&pointer, json);
711    }
712
713    #[test]
714    fn test_json_actions_type_pointer_with_parameters_missing() {
715        let json = json!({
716        "type": "pointer",
717        "actions": [
718            {"type": "pointerDown", "button": 1},
719        ]});
720        let pointer = ActionsType::Pointer {
721            parameters: PointerActionParameters {
722                pointer_type: PointerType::Mouse,
723            },
724            actions: vec![PointerActionItem::Pointer(PointerAction::Down(
725                PointerDownAction {
726                    button: 1,
727                    ..Default::default()
728                },
729            ))],
730        };
731
732        assert_de(&pointer, json);
733    }
734
735    #[test]
736    fn test_json_actions_type_pointer_with_parameters_invalid_type() {
737        let json = json!({
738        "type": "pointer",
739        "parameters": null,
740        "actions": [
741            {"type":"pointerDown", "button": 1},
742        ]});
743        assert!(serde_json::from_value::<ActionsType>(json).is_err());
744    }
745
746    #[test]
747    fn test_json_actions_type_invalid() {
748        let json = json!({"actions": [{"foo": "bar"}]});
749        assert!(serde_json::from_value::<ActionsType>(json).is_err());
750    }
751
752    #[test]
753    fn test_json_null_action_item_general() {
754        let pause =
755            NullActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
756        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
757    }
758
759    #[test]
760    fn test_json_null_action_item_invalid_type() {
761        assert!(serde_json::from_value::<NullActionItem>(json!({"type": "invalid"})).is_err());
762    }
763
764    #[test]
765    fn test_json_general_action_pause() {
766        let pause = GeneralAction::Pause(PauseAction { duration: Some(1) });
767        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
768    }
769
770    #[test]
771    fn test_json_general_action_pause_with_duration_missing() {
772        let pause = GeneralAction::Pause(PauseAction { duration: None });
773        assert_ser_de(&pause, json!({"type": "pause"}));
774    }
775
776    #[test]
777    fn test_json_general_action_pause_with_duration_null() {
778        let json = json!({"type": "pause", "duration": null});
779        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
780    }
781
782    #[test]
783    fn test_json_general_action_pause_with_duration_invalid_type() {
784        let json = json!({"type": "pause", "duration":" foo"});
785        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
786    }
787
788    #[test]
789    fn test_json_general_action_pause_with_duration_negative() {
790        let json = json!({"type": "pause", "duration": -30});
791        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
792    }
793
794    #[test]
795    fn test_json_key_action_item_general() {
796        let pause = KeyActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
797        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
798    }
799
800    #[test]
801    fn test_json_key_action_item_key() {
802        let key_down = KeyActionItem::Key(KeyAction::Down(KeyDownAction {
803            value: String::from("f"),
804        }));
805        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"}));
806    }
807
808    #[test]
809    fn test_json_key_action_item_invalid_type() {
810        assert!(serde_json::from_value::<KeyActionItem>(json!({"type": "invalid"})).is_err());
811    }
812
813    #[test]
814    fn test_json_key_action_missing_subtype() {
815        assert!(serde_json::from_value::<KeyAction>(json!({"value": "f"})).is_err());
816    }
817
818    #[test]
819    fn test_json_key_action_wrong_subtype() {
820        let json = json!({"type": "pause", "value": "f"});
821        assert!(serde_json::from_value::<KeyAction>(json).is_err());
822    }
823
824    #[test]
825    fn test_json_key_action_down() {
826        let key_down = KeyAction::Down(KeyDownAction {
827            value: "f".to_string(),
828        });
829        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"}));
830    }
831
832    #[test]
833    fn test_json_key_action_down_with_value_unicode() {
834        let key_down = KeyAction::Down(KeyDownAction {
835            value: "à".to_string(),
836        });
837        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "à"}));
838    }
839
840    #[test]
841    fn test_json_key_action_down_with_value_unicode_encoded() {
842        let key_down = KeyAction::Down(KeyDownAction {
843            value: "à".to_string(),
844        });
845        assert_de(&key_down, json!({"type": "keyDown", "value": "\u{00E0}"}));
846    }
847
848    #[test]
849    fn test_json_key_action_down_with_value_missing() {
850        assert!(serde_json::from_value::<KeyAction>(json!({"type": "keyDown"})).is_err());
851    }
852
853    #[test]
854    fn test_json_key_action_down_with_value_null() {
855        let json = json!({"type": "keyDown", "value": null});
856        assert!(serde_json::from_value::<KeyAction>(json).is_err());
857    }
858
859    #[test]
860    fn test_json_key_action_down_with_value_invalid_type() {
861        let json = json!({"type": "keyDown", "value": ["f", "o", "o"]});
862        assert!(serde_json::from_value::<KeyAction>(json).is_err());
863    }
864
865    #[test]
866    fn test_json_key_action_down_with_multiple_code_points() {
867        let json = json!({"type": "keyDown", "value": "fo"});
868        assert!(serde_json::from_value::<KeyAction>(json).is_err());
869    }
870
871    #[test]
872    fn test_json_key_action_up() {
873        let key_up = KeyAction::Up(KeyUpAction {
874            value: "f".to_string(),
875        });
876        assert_ser_de(&key_up, json!({"type": "keyUp", "value": "f"}));
877    }
878
879    #[test]
880    fn test_json_key_action_up_with_value_unicode() {
881        let key_up = KeyAction::Up(KeyUpAction {
882            value: "à".to_string(),
883        });
884        assert_ser_de(&key_up, json!({"type":"keyUp", "value": "à"}));
885    }
886
887    #[test]
888    fn test_json_key_action_up_with_value_unicode_encoded() {
889        let key_up = KeyAction::Up(KeyUpAction {
890            value: "à".to_string(),
891        });
892        assert_de(&key_up, json!({"type": "keyUp", "value": "\u{00E0}"}));
893    }
894
895    #[test]
896    fn test_json_key_action_up_with_value_missing() {
897        assert!(serde_json::from_value::<KeyAction>(json!({"type": "keyUp"})).is_err());
898    }
899
900    #[test]
901    fn test_json_key_action_up_with_value_null() {
902        let json = json!({"type": "keyUp", "value": null});
903        assert!(serde_json::from_value::<KeyAction>(json).is_err());
904    }
905
906    #[test]
907    fn test_json_key_action_up_with_value_invalid_type() {
908        let json = json!({"type": "keyUp", "value": ["f","o","o"]});
909        assert!(serde_json::from_value::<KeyAction>(json).is_err());
910    }
911
912    #[test]
913    fn test_json_key_action_up_with_multiple_code_points() {
914        let json = json!({"type": "keyUp", "value": "fo"});
915        assert!(serde_json::from_value::<KeyAction>(json).is_err());
916    }
917
918    #[test]
919    fn test_json_pointer_action_item_general() {
920        let pause =
921            PointerActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
922        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
923    }
924
925    #[test]
926    fn test_json_pointer_action_item_pointer() {
927        let cancel = PointerActionItem::Pointer(PointerAction::Cancel);
928        assert_ser_de(&cancel, json!({"type": "pointerCancel"}));
929    }
930
931    #[test]
932    fn test_json_pointer_action_item_invalid() {
933        assert!(serde_json::from_value::<PointerActionItem>(json!({"type": "invalid"})).is_err());
934    }
935
936    #[test]
937    fn test_json_pointer_action_parameters_mouse() {
938        let mouse = PointerActionParameters {
939            pointer_type: PointerType::Mouse,
940        };
941        assert_ser_de(&mouse, json!({"pointerType": "mouse"}));
942    }
943
944    #[test]
945    fn test_json_pointer_action_parameters_pen() {
946        let pen = PointerActionParameters {
947            pointer_type: PointerType::Pen,
948        };
949        assert_ser_de(&pen, json!({"pointerType": "pen"}));
950    }
951
952    #[test]
953    fn test_json_pointer_action_parameters_touch() {
954        let touch = PointerActionParameters {
955            pointer_type: PointerType::Touch,
956        };
957        assert_ser_de(&touch, json!({"pointerType": "touch"}));
958    }
959
960    #[test]
961    fn test_json_pointer_action_item_invalid_type() {
962        let json = json!({"type": "pointerInvalid"});
963        assert!(serde_json::from_value::<PointerActionItem>(json).is_err());
964    }
965
966    #[test]
967    fn test_json_pointer_action_missing_subtype() {
968        assert!(serde_json::from_value::<PointerAction>(json!({"button": 1})).is_err());
969    }
970
971    #[test]
972    fn test_json_pointer_action_invalid_subtype() {
973        let json = json!({"type": "invalid", "button": 1});
974        assert!(serde_json::from_value::<PointerAction>(json).is_err());
975    }
976
977    #[test]
978    fn test_json_pointer_action_cancel() {
979        assert_ser_de(&PointerAction::Cancel, json!({"type": "pointerCancel"}));
980    }
981
982    #[test]
983    fn test_json_pointer_action_down() {
984        let pointer_down = PointerAction::Down(PointerDownAction {
985            button: 1,
986            ..Default::default()
987        });
988        assert_ser_de(&pointer_down, json!({"type": "pointerDown", "button": 1}));
989    }
990
991    #[test]
992    fn test_json_pointer_action_down_with_button_missing() {
993        let json = json!({"type": "pointerDown"});
994        assert!(serde_json::from_value::<PointerAction>(json).is_err());
995    }
996
997    #[test]
998    fn test_json_pointer_action_down_with_button_null() {
999        let json = json!({
1000            "type": "pointerDown",
1001            "button": null,
1002        });
1003        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1004    }
1005
1006    #[test]
1007    fn test_json_pointer_action_down_with_button_invalid_type() {
1008        let json = json!({
1009            "type": "pointerDown",
1010            "button": "foo",
1011        });
1012        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1013    }
1014
1015    #[test]
1016    fn test_json_pointer_action_down_with_button_negative() {
1017        let json = json!({
1018            "type": "pointerDown",
1019            "button": -30,
1020        });
1021        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1022    }
1023
1024    #[test]
1025    fn test_json_pointer_action_move() {
1026        let json = json!({
1027            "type": "pointerMove",
1028            "duration": 100,
1029            "origin": "viewport",
1030            "x": 5.5,
1031            "y": 10.5,
1032        });
1033        let pointer_move = PointerAction::Move(PointerMoveAction {
1034            duration: Some(100),
1035            origin: PointerOrigin::Viewport,
1036            x: 5.5,
1037            y: 10.5,
1038            ..Default::default()
1039        });
1040
1041        assert_ser_de(&pointer_move, json);
1042    }
1043
1044    #[test]
1045    fn test_json_pointer_action_move_missing_subtype() {
1046        let json = json!({
1047            "duration": 100,
1048            "origin": "viewport",
1049            "x": 5.5,
1050            "y": 10.5,
1051        });
1052        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1053    }
1054
1055    #[test]
1056    fn test_json_pointer_action_move_wrong_subtype() {
1057        let json = json!({
1058            "type": "pointerUp",
1059            "duration": 100,
1060            "origin": "viewport",
1061            "x": 5.5,
1062            "y": 10.5,
1063        });
1064        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1065    }
1066
1067    #[test]
1068    fn test_json_pointer_action_move_with_duration_missing() {
1069        let json = json!({
1070            "type": "pointerMove",
1071            "origin": "viewport",
1072            "x": 5.5,
1073            "y": 10.5,
1074        });
1075        let pointer_move = PointerAction::Move(PointerMoveAction {
1076            duration: None,
1077            origin: PointerOrigin::Viewport,
1078            x: 5.5,
1079            y: 10.5,
1080            ..Default::default()
1081        });
1082
1083        assert_ser_de(&pointer_move, json);
1084    }
1085
1086    #[test]
1087    fn test_json_pointer_action_move_with_duration_null() {
1088        let json = json!({
1089            "type": "pointerMove",
1090            "duration": null,
1091            "origin": "viewport",
1092            "x": 5.5,
1093            "y": 10.5,
1094        });
1095        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1096    }
1097
1098    #[test]
1099    fn test_json_pointer_action_move_with_duration_invalid_type() {
1100        let json = json!({
1101            "type": "pointerMove",
1102            "duration": "invalid",
1103            "origin": "viewport",
1104            "x": 5.5,
1105            "y": 10.5,
1106        });
1107        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1108    }
1109
1110    #[test]
1111    fn test_json_pointer_action_move_with_duration_negative() {
1112        let json = json!({
1113            "type": "pointerMove",
1114            "duration": -30,
1115            "origin": "viewport",
1116            "x": 5.5,
1117            "y": 10.5,
1118        });
1119        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1120    }
1121
1122    #[test]
1123    fn test_json_pointer_action_move_with_origin_missing() {
1124        let json = json!({
1125            "type": "pointerMove",
1126            "duration": 100,
1127            "x": 5.5,
1128            "y": 10.5,
1129        });
1130        let pointer_move = PointerAction::Move(PointerMoveAction {
1131            duration: Some(100),
1132            origin: PointerOrigin::Viewport,
1133            x: 5.5,
1134            y: 10.5,
1135            ..Default::default()
1136        });
1137
1138        assert_de(&pointer_move, json);
1139    }
1140
1141    #[test]
1142    fn test_json_pointer_action_move_with_origin_webelement() {
1143        let json = json!({
1144            "type": "pointerMove",
1145            "duration": 100,
1146            "origin": {ELEMENT_KEY: "elem"},
1147            "x": 5.5,
1148            "y": 10.5,
1149        });
1150        let pointer_move = PointerAction::Move(PointerMoveAction {
1151            duration: Some(100),
1152            origin: PointerOrigin::Element(WebElement("elem".into())),
1153            x: 5.5,
1154            y: 10.5,
1155            ..Default::default()
1156        });
1157
1158        assert_ser_de(&pointer_move, json);
1159    }
1160
1161    #[test]
1162    fn test_json_pointer_action_move_with_origin_webelement_and_legacy_element() {
1163        let json = json!({
1164            "type": "pointerMove",
1165            "duration": 100,
1166            "origin": {ELEMENT_KEY: "elem"},
1167            "x": 5.5,
1168            "y": 10.5,
1169        });
1170        let pointer_move = PointerAction::Move(PointerMoveAction {
1171            duration: Some(100),
1172            origin: PointerOrigin::Element(WebElement("elem".into())),
1173            x: 5.5,
1174            y: 10.5,
1175            ..Default::default()
1176        });
1177
1178        assert_de(&pointer_move, json);
1179    }
1180
1181    #[test]
1182    fn test_json_pointer_action_move_with_origin_only_legacy_element() {
1183        let json = json!({
1184            "type": "pointerMove",
1185            "duration": 100,
1186            "origin": {ELEMENT_KEY: "elem"},
1187            "x": 5,
1188            "y": 10,
1189        });
1190        assert!(serde_json::from_value::<PointerOrigin>(json).is_err());
1191    }
1192
1193    #[test]
1194    fn test_json_pointer_action_move_with_x_null() {
1195        let json = json!({
1196            "type": "pointerMove",
1197            "duration": 100,
1198            "origin": "viewport",
1199            "x": null,
1200            "y": 10,
1201        });
1202        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1203    }
1204
1205    #[test]
1206    fn test_json_pointer_action_move_with_x_invalid_type() {
1207        let json = json!({
1208            "type": "pointerMove",
1209            "duration": 100,
1210            "origin": "viewport",
1211            "x": "invalid",
1212            "y": 10,
1213        });
1214        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1215    }
1216
1217    #[test]
1218    fn test_json_pointer_action_move_with_y_null() {
1219        let json = json!({
1220            "type": "pointerMove",
1221            "duration": 100,
1222            "origin": "viewport",
1223            "x": 5,
1224            "y": null,
1225        });
1226        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1227    }
1228
1229    #[test]
1230    fn test_json_pointer_action_move_with_y_invalid_type() {
1231        let json = json!({
1232            "type": "pointerMove",
1233            "duration": 100,
1234            "origin": "viewport",
1235            "x": 5,
1236            "y": "invalid",
1237        });
1238        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1239    }
1240
1241    #[test]
1242    fn test_json_pointer_action_up() {
1243        let pointer_up = PointerAction::Up(PointerUpAction {
1244            button: 1,
1245            ..Default::default()
1246        });
1247        assert_ser_de(&pointer_up, json!({"type": "pointerUp", "button": 1}));
1248    }
1249
1250    #[test]
1251    fn test_json_pointer_action_up_with_button_missing() {
1252        assert!(serde_json::from_value::<PointerAction>(json!({"type": "pointerUp"})).is_err());
1253    }
1254
1255    #[test]
1256    fn test_json_pointer_action_up_with_button_null() {
1257        let json = json!({
1258            "type": "pointerUp",
1259            "button": null,
1260        });
1261        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1262    }
1263
1264    #[test]
1265    fn test_json_pointer_action_up_with_button_invalid_type() {
1266        let json = json!({
1267            "type": "pointerUp",
1268            "button": "foo",
1269        });
1270        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1271    }
1272
1273    #[test]
1274    fn test_json_pointer_action_up_with_button_negative() {
1275        let json = json!({
1276            "type": "pointerUp",
1277            "button": -30,
1278        });
1279        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1280    }
1281
1282    #[test]
1283    fn test_json_pointer_origin_pointer() {
1284        assert_ser_de(&PointerOrigin::Pointer, json!("pointer"));
1285    }
1286
1287    #[test]
1288    fn test_json_pointer_origin_viewport() {
1289        assert_ser_de(&PointerOrigin::Viewport, json!("viewport"));
1290    }
1291
1292    #[test]
1293    fn test_json_pointer_origin_web_element() {
1294        let element = PointerOrigin::Element(WebElement("elem".into()));
1295        assert_ser_de(&element, json!({ELEMENT_KEY: "elem"}));
1296    }
1297
1298    #[test]
1299    fn test_json_pointer_origin_invalid_type() {
1300        assert!(serde_json::from_value::<PointerOrigin>(json!("invalid")).is_err());
1301    }
1302
1303    #[test]
1304    fn test_json_pointer_type_mouse() {
1305        assert_ser_de(&PointerType::Mouse, json!("mouse"));
1306    }
1307
1308    #[test]
1309    fn test_json_pointer_type_pen() {
1310        assert_ser_de(&PointerType::Pen, json!("pen"));
1311    }
1312
1313    #[test]
1314    fn test_json_pointer_type_touch() {
1315        assert_ser_de(&PointerType::Touch, json!("touch"));
1316    }
1317
1318    #[test]
1319    fn test_json_pointer_type_invalid_type() {
1320        assert!(serde_json::from_value::<PointerType>(json!("invalid")).is_err());
1321    }
1322
1323    #[test]
1324    fn test_pointer_properties() {
1325        // Ideally these would be seperate tests, but it was too much boilerplate to write
1326        // and adding a macro seemed like overkill.
1327        for actionType in ["pointerUp", "pointerDown", "pointerMove"] {
1328            for (prop_name, value, is_valid) in [
1329                ("pressure", Value::from(0), true),
1330                ("pressure", Value::from(0.5), true),
1331                ("pressure", Value::from(1), true),
1332                ("pressure", Value::from(1.1), false),
1333                ("pressure", Value::from(-0.1), false),
1334                ("tangentialPressure", Value::from(-1), true),
1335                ("tangentialPressure", Value::from(0), true),
1336                ("tangentialPressure", Value::from(1.0), true),
1337                ("tangentialPressure", Value::from(-1.1), false),
1338                ("tangentialPressure", Value::from(1.1), false),
1339                ("tiltX", Value::from(-90), true),
1340                ("tiltX", Value::from(0), true),
1341                ("tiltX", Value::from(45), true),
1342                ("tiltX", Value::from(90), true),
1343                ("tiltX", Value::from(0.5), false),
1344                ("tiltX", Value::from(-91), false),
1345                ("tiltX", Value::from(91), false),
1346                ("tiltY", Value::from(-90), true),
1347                ("tiltY", Value::from(0), true),
1348                ("tiltY", Value::from(45), true),
1349                ("tiltY", Value::from(90), true),
1350                ("tiltY", Value::from(0.5), false),
1351                ("tiltY", Value::from(-91), false),
1352                ("tiltY", Value::from(91), false),
1353                ("twist", Value::from(0), true),
1354                ("twist", Value::from(180), true),
1355                ("twist", Value::from(359), true),
1356                ("twist", Value::from(360), false),
1357                ("twist", Value::from(-1), false),
1358                ("twist", Value::from(23.5), false),
1359                ("altitudeAngle", Value::from(0), true),
1360                ("altitudeAngle", Value::from(f64::consts::FRAC_PI_4), true),
1361                ("altitudeAngle", Value::from(f64::consts::FRAC_PI_2), true),
1362                (
1363                    "altitudeAngle",
1364                    Value::from(f64::consts::FRAC_PI_2 + 0.1),
1365                    false,
1366                ),
1367                ("altitudeAngle", Value::from(-f64::consts::FRAC_PI_4), false),
1368                ("azimuthAngle", Value::from(0), true),
1369                ("azimuthAngle", Value::from(f64::consts::PI), true),
1370                ("azimuthAngle", Value::from(f64::consts::TAU), true),
1371                ("azimuthAngle", Value::from(f64::consts::TAU + 0.01), false),
1372                ("azimuthAngle", Value::from(-f64::consts::FRAC_PI_4), false),
1373            ] {
1374                let mut json = serde_json::Map::new();
1375                json.insert("type".into(), actionType.into());
1376                if actionType != "pointerMove" {
1377                    json.insert("button".into(), Value::from(0));
1378                } else {
1379                    json.insert("x".into(), Value::from(0));
1380                    json.insert("y".into(), Value::from(0));
1381                }
1382                json.insert(prop_name.into(), value);
1383                println!("{:?}", json);
1384                let deserialized = serde_json::from_value::<PointerAction>(json.into());
1385                if is_valid {
1386                    assert!(deserialized.is_ok());
1387                } else {
1388                    assert!(deserialized.is_err());
1389                }
1390            }
1391        }
1392    }
1393}