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