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/// ```ignore
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)]
32pub struct TapOptions {
33    /// Whether to bypass actionability checks
34    pub force: Option<bool>,
35    /// Modifier keys to press during tap
36    pub modifiers: Option<Vec<KeyboardModifier>>,
37    /// Position to tap relative to element top-left corner
38    pub position: Option<Position>,
39    /// Maximum time in milliseconds
40    pub timeout: Option<f64>,
41    /// Perform actionability checks without tapping
42    pub trial: Option<bool>,
43}
44
45impl TapOptions {
46    /// Create a new builder for TapOptions
47    pub fn builder() -> TapOptionsBuilder {
48        TapOptionsBuilder::default()
49    }
50
51    /// Convert options to JSON value for protocol
52    pub(crate) fn to_json(&self) -> serde_json::Value {
53        let mut json = serde_json::json!({});
54
55        if let Some(force) = self.force {
56            json["force"] = serde_json::json!(force);
57        }
58
59        if let Some(modifiers) = &self.modifiers {
60            json["modifiers"] = serde_json::to_value(modifiers).unwrap();
61        }
62
63        if let Some(position) = &self.position {
64            json["position"] = serde_json::to_value(position).unwrap();
65        }
66
67        // Timeout is required in Playwright 1.56.1+
68        if let Some(timeout) = self.timeout {
69            json["timeout"] = serde_json::json!(timeout);
70        } else {
71            json["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
72        }
73
74        if let Some(trial) = self.trial {
75            json["trial"] = serde_json::json!(trial);
76        }
77
78        json
79    }
80}
81
82/// Builder for TapOptions
83///
84/// Provides a fluent API for constructing tap options.
85#[derive(Debug, Clone, Default)]
86pub struct TapOptionsBuilder {
87    force: Option<bool>,
88    modifiers: Option<Vec<KeyboardModifier>>,
89    position: Option<Position>,
90    timeout: Option<f64>,
91    trial: Option<bool>,
92}
93
94impl TapOptionsBuilder {
95    /// Bypass actionability checks
96    pub fn force(mut self, force: bool) -> Self {
97        self.force = Some(force);
98        self
99    }
100
101    /// Set modifier keys to press during tap
102    pub fn modifiers(mut self, modifiers: Vec<KeyboardModifier>) -> Self {
103        self.modifiers = Some(modifiers);
104        self
105    }
106
107    /// Set position to tap relative to element top-left corner
108    pub fn position(mut self, position: Position) -> Self {
109        self.position = Some(position);
110        self
111    }
112
113    /// Set timeout in milliseconds
114    pub fn timeout(mut self, timeout: f64) -> Self {
115        self.timeout = Some(timeout);
116        self
117    }
118
119    /// Perform actionability checks without tapping
120    pub fn trial(mut self, trial: bool) -> Self {
121        self.trial = Some(trial);
122        self
123    }
124
125    /// Build the TapOptions
126    pub fn build(self) -> TapOptions {
127        TapOptions {
128            force: self.force,
129            modifiers: self.modifiers,
130            position: self.position,
131            timeout: self.timeout,
132            trial: self.trial,
133        }
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn test_tap_options_default() {
143        let options = TapOptions::builder().build();
144        let json = options.to_json();
145        // timeout has a default value
146        assert!(json["timeout"].is_number());
147        // other fields are absent
148        assert!(json.get("force").is_none());
149        assert!(json.get("trial").is_none());
150    }
151
152    #[test]
153    fn test_tap_options_force() {
154        let options = TapOptions::builder().force(true).build();
155        let json = options.to_json();
156        assert_eq!(json["force"], true);
157    }
158
159    #[test]
160    fn test_tap_options_timeout() {
161        let options = TapOptions::builder().timeout(5000.0).build();
162        let json = options.to_json();
163        assert_eq!(json["timeout"], 5000.0);
164    }
165
166    #[test]
167    fn test_tap_options_trial() {
168        let options = TapOptions::builder().trial(true).build();
169        let json = options.to_json();
170        assert_eq!(json["trial"], true);
171    }
172
173    #[test]
174    fn test_tap_options_position() {
175        let options = TapOptions::builder()
176            .position(Position { x: 10.0, y: 20.0 })
177            .build();
178        let json = options.to_json();
179        assert_eq!(json["position"]["x"], 10.0);
180        assert_eq!(json["position"]["y"], 20.0);
181    }
182}