Skip to main content

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        && !(0f64..=1.0).contains(&value) {
434            return Err(de::Error::custom(format!("{} is outside range 0-1", value)));
435        };
436    Ok(opt_value)
437}
438
439fn deserialize_to_tangential_pressure<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
440where
441    D: Deserializer<'de>,
442{
443    let opt_value = deserialize_to_option_f64(deserializer)?;
444    if let Some(value) = opt_value
445        && !(-1.0..=1.0).contains(&value) {
446            return Err(de::Error::custom(format!(
447                "{} is outside range -1-1",
448                value
449            )));
450        };
451    Ok(opt_value)
452}
453
454fn deserialize_to_tilt<'de, D>(deserializer: D) -> Result<Option<i64>, D::Error>
455where
456    D: Deserializer<'de>,
457{
458    let opt_value = deserialize_to_option_i64(deserializer)?;
459    if let Some(value) = opt_value
460        && !(-90..=90).contains(&value) {
461            return Err(de::Error::custom(format!(
462                "{} is outside range -90-90",
463                value
464            )));
465        };
466    Ok(opt_value)
467}
468
469fn deserialize_to_twist<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
470where
471    D: Deserializer<'de>,
472{
473    let opt_value = deserialize_to_option_u64(deserializer)?;
474    if let Some(value) = opt_value
475        && !(0..=359).contains(&value) {
476            return Err(de::Error::custom(format!(
477                "{} is outside range 0-359",
478                value
479            )));
480        };
481    Ok(opt_value)
482}
483
484fn deserialize_to_altitude_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
485where
486    D: Deserializer<'de>,
487{
488    let opt_value = deserialize_to_option_f64(deserializer)?;
489    if let Some(value) = opt_value
490        && !(0f64..=f64::consts::FRAC_PI_2).contains(&value) {
491            return Err(de::Error::custom(format!(
492                "{} is outside range 0-PI/2",
493                value
494            )));
495        };
496    Ok(opt_value)
497}
498
499fn deserialize_to_azimuth_angle<'de, D>(deserializer: D) -> Result<Option<f64>, D::Error>
500where
501    D: Deserializer<'de>,
502{
503    let opt_value = deserialize_to_option_f64(deserializer)?;
504    if let Some(value) = opt_value
505        && !(0f64..=f64::consts::TAU).contains(&value) {
506            return Err(de::Error::custom(format!(
507                "{} is outside range 0-2*PI",
508                value
509            )));
510        };
511    Ok(opt_value)
512}
513
514#[cfg(test)]
515mod test {
516    use super::*;
517    use crate::test::{assert_de, assert_ser_de};
518    use serde_json::{self, json, Value};
519
520    #[test]
521    fn test_json_action_sequence_null() {
522        let json = json!({
523            "id": "some_key",
524            "type": "none",
525            "actions": [{
526                "type": "pause",
527                "duration": 1,
528            }]
529        });
530        let seq = ActionSequence {
531            id: "some_key".into(),
532            actions: ActionsType::Null {
533                actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction {
534                    duration: Some(1),
535                }))],
536            },
537        };
538
539        assert_ser_de(&seq, json);
540    }
541
542    #[test]
543    fn test_json_action_sequence_key() {
544        let json = json!({
545            "id": "some_key",
546            "type": "key",
547            "actions": [
548                {"type": "keyDown", "value": "f"},
549            ],
550        });
551        let seq = ActionSequence {
552            id: "some_key".into(),
553            actions: ActionsType::Key {
554                actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction {
555                    value: String::from("f"),
556                }))],
557            },
558        };
559
560        assert_ser_de(&seq, json);
561    }
562
563    #[test]
564    fn test_json_action_sequence_pointer() {
565        let json = json!({
566            "id": "some_pointer",
567            "type": "pointer",
568            "parameters": {
569                "pointerType": "mouse"
570            },
571            "actions": [
572                {"type": "pointerDown", "button": 0},
573                {"type": "pointerMove", "origin": "pointer", "x": 10.5, "y": 20.5},
574                {"type": "pointerUp", "button": 0},
575            ]
576        });
577        let seq = ActionSequence {
578            id: "some_pointer".into(),
579            actions: ActionsType::Pointer {
580                parameters: PointerActionParameters {
581                    pointer_type: PointerType::Mouse,
582                },
583                actions: vec![
584                    PointerActionItem::Pointer(PointerAction::Down(PointerDownAction {
585                        button: 0,
586                        ..Default::default()
587                    })),
588                    PointerActionItem::Pointer(PointerAction::Move(PointerMoveAction {
589                        origin: PointerOrigin::Pointer,
590                        duration: None,
591                        x: 10.5,
592                        y: 20.5,
593                        ..Default::default()
594                    })),
595                    PointerActionItem::Pointer(PointerAction::Up(PointerUpAction {
596                        button: 0,
597                        ..Default::default()
598                    })),
599                ],
600            },
601        };
602
603        assert_ser_de(&seq, json);
604    }
605
606    #[test]
607    fn test_json_action_sequence_id_missing() {
608        let json = json!({
609            "type": "key",
610            "actions": [],
611        });
612        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
613    }
614
615    #[test]
616    fn test_json_action_sequence_id_null() {
617        let json = json!({
618            "id": null,
619            "type": "key",
620            "actions": [],
621        });
622        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
623    }
624
625    #[test]
626    fn test_json_action_sequence_actions_missing() {
627        assert!(serde_json::from_value::<ActionSequence>(json!({"id": "3"})).is_err());
628    }
629
630    #[test]
631    fn test_json_action_sequence_actions_null() {
632        let json = json!({
633            "id": "3",
634            "actions": null,
635        });
636        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
637    }
638
639    #[test]
640    fn test_json_action_sequence_actions_invalid_type() {
641        let json = json!({
642            "id": "3",
643            "actions": "foo",
644        });
645        assert!(serde_json::from_value::<ActionSequence>(json).is_err());
646    }
647
648    #[test]
649    fn test_json_actions_type_null() {
650        let json = json!({
651            "type": "none",
652            "actions": [{
653                "type": "pause",
654                "duration": 1,
655            }],
656        });
657        let null = ActionsType::Null {
658            actions: vec![NullActionItem::General(GeneralAction::Pause(PauseAction {
659                duration: Some(1),
660            }))],
661        };
662
663        assert_ser_de(&null, json);
664    }
665
666    #[test]
667    fn test_json_actions_type_key() {
668        let json = json!({
669            "type": "key",
670            "actions": [{
671                "type": "keyDown",
672                "value": "f",
673            }],
674        });
675        let key = ActionsType::Key {
676            actions: vec![KeyActionItem::Key(KeyAction::Down(KeyDownAction {
677                value: String::from("f"),
678            }))],
679        };
680
681        assert_ser_de(&key, json);
682    }
683
684    #[test]
685    fn test_json_actions_type_pointer() {
686        let json = json!({
687        "type": "pointer",
688        "parameters": {"pointerType": "mouse"},
689        "actions": [
690            {"type": "pointerDown", "button": 1},
691        ]});
692        let pointer = ActionsType::Pointer {
693            parameters: PointerActionParameters {
694                pointer_type: PointerType::Mouse,
695            },
696            actions: vec![PointerActionItem::Pointer(PointerAction::Down(
697                PointerDownAction {
698                    button: 1,
699                    ..Default::default()
700                },
701            ))],
702        };
703
704        assert_ser_de(&pointer, json);
705    }
706
707    #[test]
708    fn test_json_actions_type_pointer_with_parameters_missing() {
709        let json = json!({
710        "type": "pointer",
711        "actions": [
712            {"type": "pointerDown", "button": 1},
713        ]});
714        let pointer = ActionsType::Pointer {
715            parameters: PointerActionParameters {
716                pointer_type: PointerType::Mouse,
717            },
718            actions: vec![PointerActionItem::Pointer(PointerAction::Down(
719                PointerDownAction {
720                    button: 1,
721                    ..Default::default()
722                },
723            ))],
724        };
725
726        assert_de(&pointer, json);
727    }
728
729    #[test]
730    fn test_json_actions_type_pointer_with_parameters_invalid_type() {
731        let json = json!({
732        "type": "pointer",
733        "parameters": null,
734        "actions": [
735            {"type":"pointerDown", "button": 1},
736        ]});
737        assert!(serde_json::from_value::<ActionsType>(json).is_err());
738    }
739
740    #[test]
741    fn test_json_actions_type_invalid() {
742        let json = json!({"actions": [{"foo": "bar"}]});
743        assert!(serde_json::from_value::<ActionsType>(json).is_err());
744    }
745
746    #[test]
747    fn test_json_null_action_item_general() {
748        let pause =
749            NullActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
750        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
751    }
752
753    #[test]
754    fn test_json_null_action_item_invalid_type() {
755        assert!(serde_json::from_value::<NullActionItem>(json!({"type": "invalid"})).is_err());
756    }
757
758    #[test]
759    fn test_json_general_action_pause() {
760        let pause = GeneralAction::Pause(PauseAction { duration: Some(1) });
761        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
762    }
763
764    #[test]
765    fn test_json_general_action_pause_with_duration_missing() {
766        let pause = GeneralAction::Pause(PauseAction { duration: None });
767        assert_ser_de(&pause, json!({"type": "pause"}));
768    }
769
770    #[test]
771    fn test_json_general_action_pause_with_duration_null() {
772        let json = json!({"type": "pause", "duration": null});
773        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
774    }
775
776    #[test]
777    fn test_json_general_action_pause_with_duration_invalid_type() {
778        let json = json!({"type": "pause", "duration":" foo"});
779        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
780    }
781
782    #[test]
783    fn test_json_general_action_pause_with_duration_negative() {
784        let json = json!({"type": "pause", "duration": -30});
785        assert!(serde_json::from_value::<GeneralAction>(json).is_err());
786    }
787
788    #[test]
789    fn test_json_key_action_item_general() {
790        let pause = KeyActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
791        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
792    }
793
794    #[test]
795    fn test_json_key_action_item_key() {
796        let key_down = KeyActionItem::Key(KeyAction::Down(KeyDownAction {
797            value: String::from("f"),
798        }));
799        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"}));
800    }
801
802    #[test]
803    fn test_json_key_action_item_invalid_type() {
804        assert!(serde_json::from_value::<KeyActionItem>(json!({"type": "invalid"})).is_err());
805    }
806
807    #[test]
808    fn test_json_key_action_missing_subtype() {
809        assert!(serde_json::from_value::<KeyAction>(json!({"value": "f"})).is_err());
810    }
811
812    #[test]
813    fn test_json_key_action_wrong_subtype() {
814        let json = json!({"type": "pause", "value": "f"});
815        assert!(serde_json::from_value::<KeyAction>(json).is_err());
816    }
817
818    #[test]
819    fn test_json_key_action_down() {
820        let key_down = KeyAction::Down(KeyDownAction {
821            value: "f".to_string(),
822        });
823        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "f"}));
824    }
825
826    #[test]
827    fn test_json_key_action_down_with_value_unicode() {
828        let key_down = KeyAction::Down(KeyDownAction {
829            value: "à".to_string(),
830        });
831        assert_ser_de(&key_down, json!({"type": "keyDown", "value": "à"}));
832    }
833
834    #[test]
835    fn test_json_key_action_down_with_value_unicode_encoded() {
836        let key_down = KeyAction::Down(KeyDownAction {
837            value: "à".to_string(),
838        });
839        assert_de(&key_down, json!({"type": "keyDown", "value": "\u{00E0}"}));
840    }
841
842    #[test]
843    fn test_json_key_action_down_with_value_missing() {
844        assert!(serde_json::from_value::<KeyAction>(json!({"type": "keyDown"})).is_err());
845    }
846
847    #[test]
848    fn test_json_key_action_down_with_value_null() {
849        let json = json!({"type": "keyDown", "value": null});
850        assert!(serde_json::from_value::<KeyAction>(json).is_err());
851    }
852
853    #[test]
854    fn test_json_key_action_down_with_value_invalid_type() {
855        let json = json!({"type": "keyDown", "value": ["f", "o", "o"]});
856        assert!(serde_json::from_value::<KeyAction>(json).is_err());
857    }
858
859    #[test]
860    fn test_json_key_action_down_with_multiple_code_points() {
861        let json = json!({"type": "keyDown", "value": "fo"});
862        assert!(serde_json::from_value::<KeyAction>(json).is_err());
863    }
864
865    #[test]
866    fn test_json_key_action_up() {
867        let key_up = KeyAction::Up(KeyUpAction {
868            value: "f".to_string(),
869        });
870        assert_ser_de(&key_up, json!({"type": "keyUp", "value": "f"}));
871    }
872
873    #[test]
874    fn test_json_key_action_up_with_value_unicode() {
875        let key_up = KeyAction::Up(KeyUpAction {
876            value: "à".to_string(),
877        });
878        assert_ser_de(&key_up, json!({"type":"keyUp", "value": "à"}));
879    }
880
881    #[test]
882    fn test_json_key_action_up_with_value_unicode_encoded() {
883        let key_up = KeyAction::Up(KeyUpAction {
884            value: "à".to_string(),
885        });
886        assert_de(&key_up, json!({"type": "keyUp", "value": "\u{00E0}"}));
887    }
888
889    #[test]
890    fn test_json_key_action_up_with_value_missing() {
891        assert!(serde_json::from_value::<KeyAction>(json!({"type": "keyUp"})).is_err());
892    }
893
894    #[test]
895    fn test_json_key_action_up_with_value_null() {
896        let json = json!({"type": "keyUp", "value": null});
897        assert!(serde_json::from_value::<KeyAction>(json).is_err());
898    }
899
900    #[test]
901    fn test_json_key_action_up_with_value_invalid_type() {
902        let json = json!({"type": "keyUp", "value": ["f","o","o"]});
903        assert!(serde_json::from_value::<KeyAction>(json).is_err());
904    }
905
906    #[test]
907    fn test_json_key_action_up_with_multiple_code_points() {
908        let json = json!({"type": "keyUp", "value": "fo"});
909        assert!(serde_json::from_value::<KeyAction>(json).is_err());
910    }
911
912    #[test]
913    fn test_json_pointer_action_item_general() {
914        let pause =
915            PointerActionItem::General(GeneralAction::Pause(PauseAction { duration: Some(1) }));
916        assert_ser_de(&pause, json!({"type": "pause", "duration": 1}));
917    }
918
919    #[test]
920    fn test_json_pointer_action_item_pointer() {
921        let cancel = PointerActionItem::Pointer(PointerAction::Cancel);
922        assert_ser_de(&cancel, json!({"type": "pointerCancel"}));
923    }
924
925    #[test]
926    fn test_json_pointer_action_item_invalid() {
927        assert!(serde_json::from_value::<PointerActionItem>(json!({"type": "invalid"})).is_err());
928    }
929
930    #[test]
931    fn test_json_pointer_action_parameters_mouse() {
932        let mouse = PointerActionParameters {
933            pointer_type: PointerType::Mouse,
934        };
935        assert_ser_de(&mouse, json!({"pointerType": "mouse"}));
936    }
937
938    #[test]
939    fn test_json_pointer_action_parameters_pen() {
940        let pen = PointerActionParameters {
941            pointer_type: PointerType::Pen,
942        };
943        assert_ser_de(&pen, json!({"pointerType": "pen"}));
944    }
945
946    #[test]
947    fn test_json_pointer_action_parameters_touch() {
948        let touch = PointerActionParameters {
949            pointer_type: PointerType::Touch,
950        };
951        assert_ser_de(&touch, json!({"pointerType": "touch"}));
952    }
953
954    #[test]
955    fn test_json_pointer_action_item_invalid_type() {
956        let json = json!({"type": "pointerInvalid"});
957        assert!(serde_json::from_value::<PointerActionItem>(json).is_err());
958    }
959
960    #[test]
961    fn test_json_pointer_action_missing_subtype() {
962        assert!(serde_json::from_value::<PointerAction>(json!({"button": 1})).is_err());
963    }
964
965    #[test]
966    fn test_json_pointer_action_invalid_subtype() {
967        let json = json!({"type": "invalid", "button": 1});
968        assert!(serde_json::from_value::<PointerAction>(json).is_err());
969    }
970
971    #[test]
972    fn test_json_pointer_action_cancel() {
973        assert_ser_de(&PointerAction::Cancel, json!({"type": "pointerCancel"}));
974    }
975
976    #[test]
977    fn test_json_pointer_action_down() {
978        let pointer_down = PointerAction::Down(PointerDownAction {
979            button: 1,
980            ..Default::default()
981        });
982        assert_ser_de(&pointer_down, json!({"type": "pointerDown", "button": 1}));
983    }
984
985    #[test]
986    fn test_json_pointer_action_down_with_button_missing() {
987        let json = json!({"type": "pointerDown"});
988        assert!(serde_json::from_value::<PointerAction>(json).is_err());
989    }
990
991    #[test]
992    fn test_json_pointer_action_down_with_button_null() {
993        let json = json!({
994            "type": "pointerDown",
995            "button": null,
996        });
997        assert!(serde_json::from_value::<PointerAction>(json).is_err());
998    }
999
1000    #[test]
1001    fn test_json_pointer_action_down_with_button_invalid_type() {
1002        let json = json!({
1003            "type": "pointerDown",
1004            "button": "foo",
1005        });
1006        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1007    }
1008
1009    #[test]
1010    fn test_json_pointer_action_down_with_button_negative() {
1011        let json = json!({
1012            "type": "pointerDown",
1013            "button": -30,
1014        });
1015        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1016    }
1017
1018    #[test]
1019    fn test_json_pointer_action_move() {
1020        let json = json!({
1021            "type": "pointerMove",
1022            "duration": 100,
1023            "origin": "viewport",
1024            "x": 5.5,
1025            "y": 10.5,
1026        });
1027        let pointer_move = PointerAction::Move(PointerMoveAction {
1028            duration: Some(100),
1029            origin: PointerOrigin::Viewport,
1030            x: 5.5,
1031            y: 10.5,
1032            ..Default::default()
1033        });
1034
1035        assert_ser_de(&pointer_move, json);
1036    }
1037
1038    #[test]
1039    fn test_json_pointer_action_move_missing_subtype() {
1040        let json = json!({
1041            "duration": 100,
1042            "origin": "viewport",
1043            "x": 5.5,
1044            "y": 10.5,
1045        });
1046        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1047    }
1048
1049    #[test]
1050    fn test_json_pointer_action_move_wrong_subtype() {
1051        let json = json!({
1052            "type": "pointerUp",
1053            "duration": 100,
1054            "origin": "viewport",
1055            "x": 5.5,
1056            "y": 10.5,
1057        });
1058        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1059    }
1060
1061    #[test]
1062    fn test_json_pointer_action_move_with_duration_missing() {
1063        let json = json!({
1064            "type": "pointerMove",
1065            "origin": "viewport",
1066            "x": 5.5,
1067            "y": 10.5,
1068        });
1069        let pointer_move = PointerAction::Move(PointerMoveAction {
1070            duration: None,
1071            origin: PointerOrigin::Viewport,
1072            x: 5.5,
1073            y: 10.5,
1074            ..Default::default()
1075        });
1076
1077        assert_ser_de(&pointer_move, json);
1078    }
1079
1080    #[test]
1081    fn test_json_pointer_action_move_with_duration_null() {
1082        let json = json!({
1083            "type": "pointerMove",
1084            "duration": null,
1085            "origin": "viewport",
1086            "x": 5.5,
1087            "y": 10.5,
1088        });
1089        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1090    }
1091
1092    #[test]
1093    fn test_json_pointer_action_move_with_duration_invalid_type() {
1094        let json = json!({
1095            "type": "pointerMove",
1096            "duration": "invalid",
1097            "origin": "viewport",
1098            "x": 5.5,
1099            "y": 10.5,
1100        });
1101        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1102    }
1103
1104    #[test]
1105    fn test_json_pointer_action_move_with_duration_negative() {
1106        let json = json!({
1107            "type": "pointerMove",
1108            "duration": -30,
1109            "origin": "viewport",
1110            "x": 5.5,
1111            "y": 10.5,
1112        });
1113        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1114    }
1115
1116    #[test]
1117    fn test_json_pointer_action_move_with_origin_missing() {
1118        let json = json!({
1119            "type": "pointerMove",
1120            "duration": 100,
1121            "x": 5.5,
1122            "y": 10.5,
1123        });
1124        let pointer_move = PointerAction::Move(PointerMoveAction {
1125            duration: Some(100),
1126            origin: PointerOrigin::Viewport,
1127            x: 5.5,
1128            y: 10.5,
1129            ..Default::default()
1130        });
1131
1132        assert_de(&pointer_move, json);
1133    }
1134
1135    #[test]
1136    fn test_json_pointer_action_move_with_origin_webelement() {
1137        let json = json!({
1138            "type": "pointerMove",
1139            "duration": 100,
1140            "origin": {ELEMENT_KEY: "elem"},
1141            "x": 5.5,
1142            "y": 10.5,
1143        });
1144        let pointer_move = PointerAction::Move(PointerMoveAction {
1145            duration: Some(100),
1146            origin: PointerOrigin::Element(WebElement("elem".into())),
1147            x: 5.5,
1148            y: 10.5,
1149            ..Default::default()
1150        });
1151
1152        assert_ser_de(&pointer_move, json);
1153    }
1154
1155    #[test]
1156    fn test_json_pointer_action_move_with_origin_webelement_and_legacy_element() {
1157        let json = json!({
1158            "type": "pointerMove",
1159            "duration": 100,
1160            "origin": {ELEMENT_KEY: "elem"},
1161            "x": 5.5,
1162            "y": 10.5,
1163        });
1164        let pointer_move = PointerAction::Move(PointerMoveAction {
1165            duration: Some(100),
1166            origin: PointerOrigin::Element(WebElement("elem".into())),
1167            x: 5.5,
1168            y: 10.5,
1169            ..Default::default()
1170        });
1171
1172        assert_de(&pointer_move, json);
1173    }
1174
1175    #[test]
1176    fn test_json_pointer_action_move_with_origin_only_legacy_element() {
1177        let json = json!({
1178            "type": "pointerMove",
1179            "duration": 100,
1180            "origin": {ELEMENT_KEY: "elem"},
1181            "x": 5,
1182            "y": 10,
1183        });
1184        assert!(serde_json::from_value::<PointerOrigin>(json).is_err());
1185    }
1186
1187    #[test]
1188    fn test_json_pointer_action_move_with_x_null() {
1189        let json = json!({
1190            "type": "pointerMove",
1191            "duration": 100,
1192            "origin": "viewport",
1193            "x": null,
1194            "y": 10,
1195        });
1196        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1197    }
1198
1199    #[test]
1200    fn test_json_pointer_action_move_with_x_invalid_type() {
1201        let json = json!({
1202            "type": "pointerMove",
1203            "duration": 100,
1204            "origin": "viewport",
1205            "x": "invalid",
1206            "y": 10,
1207        });
1208        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1209    }
1210
1211    #[test]
1212    fn test_json_pointer_action_move_with_y_null() {
1213        let json = json!({
1214            "type": "pointerMove",
1215            "duration": 100,
1216            "origin": "viewport",
1217            "x": 5,
1218            "y": null,
1219        });
1220        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1221    }
1222
1223    #[test]
1224    fn test_json_pointer_action_move_with_y_invalid_type() {
1225        let json = json!({
1226            "type": "pointerMove",
1227            "duration": 100,
1228            "origin": "viewport",
1229            "x": 5,
1230            "y": "invalid",
1231        });
1232        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1233    }
1234
1235    #[test]
1236    fn test_json_pointer_action_up() {
1237        let pointer_up = PointerAction::Up(PointerUpAction {
1238            button: 1,
1239            ..Default::default()
1240        });
1241        assert_ser_de(&pointer_up, json!({"type": "pointerUp", "button": 1}));
1242    }
1243
1244    #[test]
1245    fn test_json_pointer_action_up_with_button_missing() {
1246        assert!(serde_json::from_value::<PointerAction>(json!({"type": "pointerUp"})).is_err());
1247    }
1248
1249    #[test]
1250    fn test_json_pointer_action_up_with_button_null() {
1251        let json = json!({
1252            "type": "pointerUp",
1253            "button": null,
1254        });
1255        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1256    }
1257
1258    #[test]
1259    fn test_json_pointer_action_up_with_button_invalid_type() {
1260        let json = json!({
1261            "type": "pointerUp",
1262            "button": "foo",
1263        });
1264        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1265    }
1266
1267    #[test]
1268    fn test_json_pointer_action_up_with_button_negative() {
1269        let json = json!({
1270            "type": "pointerUp",
1271            "button": -30,
1272        });
1273        assert!(serde_json::from_value::<PointerAction>(json).is_err());
1274    }
1275
1276    #[test]
1277    fn test_json_pointer_origin_pointer() {
1278        assert_ser_de(&PointerOrigin::Pointer, json!("pointer"));
1279    }
1280
1281    #[test]
1282    fn test_json_pointer_origin_viewport() {
1283        assert_ser_de(&PointerOrigin::Viewport, json!("viewport"));
1284    }
1285
1286    #[test]
1287    fn test_json_pointer_origin_web_element() {
1288        let element = PointerOrigin::Element(WebElement("elem".into()));
1289        assert_ser_de(&element, json!({ELEMENT_KEY: "elem"}));
1290    }
1291
1292    #[test]
1293    fn test_json_pointer_origin_invalid_type() {
1294        assert!(serde_json::from_value::<PointerOrigin>(json!("invalid")).is_err());
1295    }
1296
1297    #[test]
1298    fn test_json_pointer_type_mouse() {
1299        assert_ser_de(&PointerType::Mouse, json!("mouse"));
1300    }
1301
1302    #[test]
1303    fn test_json_pointer_type_pen() {
1304        assert_ser_de(&PointerType::Pen, json!("pen"));
1305    }
1306
1307    #[test]
1308    fn test_json_pointer_type_touch() {
1309        assert_ser_de(&PointerType::Touch, json!("touch"));
1310    }
1311
1312    #[test]
1313    fn test_json_pointer_type_invalid_type() {
1314        assert!(serde_json::from_value::<PointerType>(json!("invalid")).is_err());
1315    }
1316
1317    #[test]
1318    fn test_pointer_properties() {
1319        // Ideally these would be seperate tests, but it was too much boilerplate to write
1320        // and adding a macro seemed like overkill.
1321        for actionType in ["pointerUp", "pointerDown", "pointerMove"] {
1322            for (prop_name, value, is_valid) in [
1323                ("pressure", Value::from(0), true),
1324                ("pressure", Value::from(0.5), true),
1325                ("pressure", Value::from(1), true),
1326                ("pressure", Value::from(1.1), false),
1327                ("pressure", Value::from(-0.1), false),
1328                ("tangentialPressure", Value::from(-1), true),
1329                ("tangentialPressure", Value::from(0), true),
1330                ("tangentialPressure", Value::from(1.0), true),
1331                ("tangentialPressure", Value::from(-1.1), false),
1332                ("tangentialPressure", Value::from(1.1), false),
1333                ("tiltX", Value::from(-90), true),
1334                ("tiltX", Value::from(0), true),
1335                ("tiltX", Value::from(45), true),
1336                ("tiltX", Value::from(90), true),
1337                ("tiltX", Value::from(0.5), false),
1338                ("tiltX", Value::from(-91), false),
1339                ("tiltX", Value::from(91), false),
1340                ("tiltY", Value::from(-90), true),
1341                ("tiltY", Value::from(0), true),
1342                ("tiltY", Value::from(45), true),
1343                ("tiltY", Value::from(90), true),
1344                ("tiltY", Value::from(0.5), false),
1345                ("tiltY", Value::from(-91), false),
1346                ("tiltY", Value::from(91), false),
1347                ("twist", Value::from(0), true),
1348                ("twist", Value::from(180), true),
1349                ("twist", Value::from(359), true),
1350                ("twist", Value::from(360), false),
1351                ("twist", Value::from(-1), false),
1352                ("twist", Value::from(23.5), false),
1353                ("altitudeAngle", Value::from(0), true),
1354                ("altitudeAngle", Value::from(f64::consts::FRAC_PI_4), true),
1355                ("altitudeAngle", Value::from(f64::consts::FRAC_PI_2), true),
1356                (
1357                    "altitudeAngle",
1358                    Value::from(f64::consts::FRAC_PI_2 + 0.1),
1359                    false,
1360                ),
1361                ("altitudeAngle", Value::from(-f64::consts::FRAC_PI_4), false),
1362                ("azimuthAngle", Value::from(0), true),
1363                ("azimuthAngle", Value::from(f64::consts::PI), true),
1364                ("azimuthAngle", Value::from(f64::consts::TAU), true),
1365                ("azimuthAngle", Value::from(f64::consts::TAU + 0.01), false),
1366                ("azimuthAngle", Value::from(-f64::consts::FRAC_PI_4), false),
1367            ] {
1368                let mut json = serde_json::Map::new();
1369                json.insert("type".into(), actionType.into());
1370                if actionType != "pointerMove" {
1371                    json.insert("button".into(), Value::from(0));
1372                } else {
1373                    json.insert("x".into(), Value::from(0));
1374                    json.insert("y".into(), Value::from(0));
1375                }
1376                json.insert(prop_name.into(), value);
1377                println!("{:?}", json);
1378                let deserialized = serde_json::from_value::<PointerAction>(json.into());
1379                if is_valid {
1380                    assert!(deserialized.is_ok());
1381                } else {
1382                    assert!(deserialized.is_err());
1383                }
1384            }
1385        }
1386    }
1387}