Skip to main content

playwright_rs/protocol/
drag_to.rs

1// DragToOptions and related types
2//
3// Provides configuration for drag_to actions, matching Playwright's API.
4
5use crate::protocol::click::Position;
6
7/// Options for [`Locator::drag_to()`](crate::protocol::Locator::drag_to).
8///
9/// Configuration for dragging a source element onto a target element.
10///
11/// Use the builder pattern to construct options:
12///
13/// # Example
14///
15/// ```ignore
16/// use playwright_rs::{DragToOptions, Position};
17///
18/// // Drag with custom source and target positions
19/// let options = DragToOptions::builder()
20///     .source_position(Position { x: 10.0, y: 10.0 })
21///     .target_position(Position { x: 60.0, y: 60.0 })
22///     .build();
23///
24/// // Force drag (bypass actionability checks)
25/// let options = DragToOptions::builder()
26///     .force(true)
27///     .build();
28///
29/// // Trial run (actionability checks only, don't actually drag)
30/// let options = DragToOptions::builder()
31///     .trial(true)
32///     .build();
33/// ```
34///
35/// See: <https://playwright.dev/docs/api/class-locator#locator-drag-to>
36#[derive(Debug, Clone, Default)]
37pub struct DragToOptions {
38    /// Whether to bypass actionability checks
39    pub force: Option<bool>,
40    /// Don't wait for navigation after the action
41    pub no_wait_after: Option<bool>,
42    /// Maximum time in milliseconds
43    pub timeout: Option<f64>,
44    /// Perform actionability checks without dragging
45    pub trial: Option<bool>,
46    /// Where to click on the source element (relative to top-left corner)
47    pub source_position: Option<Position>,
48    /// Where to drop on the target element (relative to top-left corner)
49    pub target_position: Option<Position>,
50}
51
52impl DragToOptions {
53    /// Create a new builder for DragToOptions
54    pub fn builder() -> DragToOptionsBuilder {
55        DragToOptionsBuilder::default()
56    }
57
58    /// Convert options to JSON value for protocol
59    pub(crate) fn to_json(&self) -> serde_json::Value {
60        let mut json = serde_json::json!({});
61
62        if let Some(force) = self.force {
63            json["force"] = serde_json::json!(force);
64        }
65
66        if let Some(no_wait_after) = self.no_wait_after {
67            json["noWaitAfter"] = serde_json::json!(no_wait_after);
68        }
69
70        // Timeout is required in Playwright 1.56.1+
71        if let Some(timeout) = self.timeout {
72            json["timeout"] = serde_json::json!(timeout);
73        } else {
74            json["timeout"] = serde_json::json!(crate::DEFAULT_TIMEOUT_MS);
75        }
76
77        if let Some(trial) = self.trial {
78            json["trial"] = serde_json::json!(trial);
79        }
80
81        if let Some(source_position) = &self.source_position {
82            json["sourcePosition"] = serde_json::to_value(source_position)
83                .expect("serialization of position cannot fail");
84        }
85
86        if let Some(target_position) = &self.target_position {
87            json["targetPosition"] = serde_json::to_value(target_position)
88                .expect("serialization of position cannot fail");
89        }
90
91        json
92    }
93}
94
95/// Builder for DragToOptions
96///
97/// Provides a fluent API for constructing drag_to options.
98#[derive(Debug, Clone, Default)]
99pub struct DragToOptionsBuilder {
100    force: Option<bool>,
101    no_wait_after: Option<bool>,
102    timeout: Option<f64>,
103    trial: Option<bool>,
104    source_position: Option<Position>,
105    target_position: Option<Position>,
106}
107
108impl DragToOptionsBuilder {
109    /// Bypass actionability checks
110    pub fn force(mut self, force: bool) -> Self {
111        self.force = Some(force);
112        self
113    }
114
115    /// Don't wait for navigation after the action
116    pub fn no_wait_after(mut self, no_wait_after: bool) -> Self {
117        self.no_wait_after = Some(no_wait_after);
118        self
119    }
120
121    /// Set timeout in milliseconds
122    pub fn timeout(mut self, timeout: f64) -> Self {
123        self.timeout = Some(timeout);
124        self
125    }
126
127    /// Perform actionability checks without dragging
128    pub fn trial(mut self, trial: bool) -> Self {
129        self.trial = Some(trial);
130        self
131    }
132
133    /// Set where to click on the source element (relative to top-left corner)
134    pub fn source_position(mut self, source_position: Position) -> Self {
135        self.source_position = Some(source_position);
136        self
137    }
138
139    /// Set where to drop on the target element (relative to top-left corner)
140    pub fn target_position(mut self, target_position: Position) -> Self {
141        self.target_position = Some(target_position);
142        self
143    }
144
145    /// Build the DragToOptions
146    pub fn build(self) -> DragToOptions {
147        DragToOptions {
148            force: self.force,
149            no_wait_after: self.no_wait_after,
150            timeout: self.timeout,
151            trial: self.trial,
152            source_position: self.source_position,
153            target_position: self.target_position,
154        }
155    }
156}
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_drag_to_options_default() {
164        let options = DragToOptions::builder().build();
165        let json = options.to_json();
166        // timeout has a default value
167        assert!(json["timeout"].is_number());
168        // other optional fields are absent
169        assert!(json.get("force").is_none());
170        assert!(json.get("trial").is_none());
171        assert!(json.get("sourcePosition").is_none());
172        assert!(json.get("targetPosition").is_none());
173    }
174
175    #[test]
176    fn test_drag_to_options_force() {
177        let options = DragToOptions::builder().force(true).build();
178        let json = options.to_json();
179        assert_eq!(json["force"], true);
180    }
181
182    #[test]
183    fn test_drag_to_options_timeout() {
184        let options = DragToOptions::builder().timeout(5000.0).build();
185        let json = options.to_json();
186        assert_eq!(json["timeout"], 5000.0);
187    }
188
189    #[test]
190    fn test_drag_to_options_trial() {
191        let options = DragToOptions::builder().trial(true).build();
192        let json = options.to_json();
193        assert_eq!(json["trial"], true);
194    }
195
196    #[test]
197    fn test_drag_to_options_positions() {
198        let options = DragToOptions::builder()
199            .source_position(Position { x: 5.0, y: 10.0 })
200            .target_position(Position { x: 50.0, y: 60.0 })
201            .build();
202        let json = options.to_json();
203        assert_eq!(json["sourcePosition"]["x"], 5.0);
204        assert_eq!(json["sourcePosition"]["y"], 10.0);
205        assert_eq!(json["targetPosition"]["x"], 50.0);
206        assert_eq!(json["targetPosition"]["y"], 60.0);
207    }
208
209    #[test]
210    fn test_drag_to_options_no_wait_after() {
211        let options = DragToOptions::builder().no_wait_after(true).build();
212        let json = options.to_json();
213        assert_eq!(json["noWaitAfter"], true);
214    }
215}