Skip to main content

playwright_rs/protocol/
tap.rs

1// Tap options and related types
2//
3// Provides configuration for tap actions, matching Playwright's API.
4// Tap is very similar to click but sends touch events instead of mouse events.
5
6use crate::protocol::click::{KeyboardModifier, Position};
7
8/// Tap options
9///
10/// Configuration options for tap actions (touch-screen taps).
11///
12/// Use the builder pattern to construct options:
13///
14/// # Example
15///
16/// ```no_run
17/// use playwright_rs::TapOptions;
18///
19/// // Tap with force (bypass actionability checks)
20/// let options = TapOptions::builder()
21///     .force(true)
22///     .build();
23///
24/// // Trial run (actionability checks only, don't actually tap)
25/// let options = TapOptions::builder()
26///     .trial(true)
27///     .build();
28/// ```
29///
30/// See: <https://playwright.dev/docs/api/class-locator#locator-tap>
31#[derive(Debug, Clone, Default, serde::Serialize)]
32#[serde(rename_all = "camelCase")]
33#[non_exhaustive]
34pub struct TapOptions {
35    /// Whether to bypass actionability checks
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub force: Option<bool>,
38    /// Modifier keys to press during tap
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub modifiers: Option<Vec<KeyboardModifier>>,
41    /// Position to tap relative to element top-left corner
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub position: Option<Position>,
44    /// Maximum time in milliseconds
45    #[serde(serialize_with = "crate::protocol::serialize_timeout_or_default")]
46    pub timeout: Option<f64>,
47    /// Perform actionability checks without tapping
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub trial: Option<bool>,
50}
51
52impl TapOptions {
53    /// Create a new builder for TapOptions
54    pub fn builder() -> TapOptionsBuilder {
55        TapOptionsBuilder::default()
56    }
57
58    /// Convert options to JSON value for protocol
59    pub(crate) fn to_json(&self) -> serde_json::Value {
60        serde_json::to_value(self).expect("TapOptions serialization cannot fail")
61    }
62}
63
64/// Builder for TapOptions
65///
66/// Provides a fluent API for constructing tap options.
67#[derive(Debug, Clone, Default)]
68pub struct TapOptionsBuilder {
69    force: Option<bool>,
70    modifiers: Option<Vec<KeyboardModifier>>,
71    position: Option<Position>,
72    timeout: Option<f64>,
73    trial: Option<bool>,
74}
75
76impl TapOptionsBuilder {
77    /// Bypass actionability checks
78    pub fn force(mut self, force: bool) -> Self {
79        self.force = Some(force);
80        self
81    }
82
83    /// Set modifier keys to press during tap
84    pub fn modifiers(mut self, modifiers: Vec<KeyboardModifier>) -> Self {
85        self.modifiers = Some(modifiers);
86        self
87    }
88
89    /// Set position to tap relative to element top-left corner
90    pub fn position(mut self, position: Position) -> Self {
91        self.position = Some(position);
92        self
93    }
94
95    /// Set timeout in milliseconds
96    pub fn timeout(mut self, timeout: f64) -> Self {
97        self.timeout = Some(timeout);
98        self
99    }
100
101    /// Perform actionability checks without tapping
102    pub fn trial(mut self, trial: bool) -> Self {
103        self.trial = Some(trial);
104        self
105    }
106
107    /// Build the TapOptions
108    pub fn build(self) -> TapOptions {
109        TapOptions {
110            force: self.force,
111            modifiers: self.modifiers,
112            position: self.position,
113            timeout: self.timeout,
114            trial: self.trial,
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122
123    #[test]
124    fn test_tap_options_default() {
125        let options = TapOptions::builder().build();
126        let json = options.to_json();
127        // timeout has a default value
128        assert!(json["timeout"].is_number());
129        // other fields are absent
130        assert!(json.get("force").is_none());
131        assert!(json.get("trial").is_none());
132    }
133
134    #[test]
135    fn test_tap_options_force() {
136        let options = TapOptions::builder().force(true).build();
137        let json = options.to_json();
138        assert_eq!(json["force"], true);
139    }
140
141    #[test]
142    fn test_tap_options_timeout() {
143        let options = TapOptions::builder().timeout(5000.0).build();
144        let json = options.to_json();
145        assert_eq!(json["timeout"], 5000.0);
146    }
147
148    #[test]
149    fn test_tap_options_trial() {
150        let options = TapOptions::builder().trial(true).build();
151        let json = options.to_json();
152        assert_eq!(json["trial"], true);
153    }
154
155    #[test]
156    fn test_tap_options_position() {
157        let options = TapOptions::builder()
158            .position(Position { x: 10.0, y: 20.0 })
159            .build();
160        let json = options.to_json();
161        assert_eq!(json["position"]["x"], 10.0);
162        assert_eq!(json["position"]["y"], 20.0);
163    }
164}