playwright_rs/api/
launch_options.rs1use crate::protocol::proxy::ProxySettings;
7use serde::{Deserialize, Serialize};
8use serde_json::{Value, json};
9use std::collections::HashMap;
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct LaunchOptions {
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub args: Option<Vec<String>>,
20
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub channel: Option<String>,
24
25 #[serde(skip_serializing_if = "Option::is_none")]
27 pub chromium_sandbox: Option<bool>,
28
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub devtools: Option<bool>,
32
33 #[serde(skip_serializing_if = "Option::is_none")]
35 pub downloads_path: Option<String>,
36
37 #[serde(skip_serializing_if = "Option::is_none")]
39 pub env: Option<HashMap<String, String>>,
40
41 #[serde(skip_serializing_if = "Option::is_none")]
43 pub executable_path: Option<String>,
44
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub firefox_user_prefs: Option<HashMap<String, Value>>,
48
49 #[serde(skip_serializing_if = "Option::is_none")]
51 pub handle_sighup: Option<bool>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
55 pub handle_sigint: Option<bool>,
56
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub handle_sigterm: Option<bool>,
60
61 #[serde(skip_serializing_if = "Option::is_none")]
63 pub headless: Option<bool>,
64
65 #[serde(skip_serializing_if = "Option::is_none")]
67 pub ignore_default_args: Option<IgnoreDefaultArgs>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
71 pub proxy: Option<ProxySettings>,
72
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub slow_mo: Option<f64>,
76
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub timeout: Option<f64>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub traces_dir: Option<String>,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88#[serde(untagged)]
89pub enum IgnoreDefaultArgs {
90 Bool(bool),
92 Array(Vec<String>),
94}
95
96impl LaunchOptions {
97 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn args(mut self, args: Vec<String>) -> Self {
104 self.args = Some(args);
105 self
106 }
107
108 pub fn channel(mut self, channel: String) -> Self {
110 self.channel = Some(channel);
111 self
112 }
113
114 pub fn chromium_sandbox(mut self, enabled: bool) -> Self {
116 self.chromium_sandbox = Some(enabled);
117 self
118 }
119
120 pub fn devtools(mut self, enabled: bool) -> Self {
122 self.devtools = Some(enabled);
123 self
124 }
125
126 pub fn downloads_path(mut self, path: String) -> Self {
128 self.downloads_path = Some(path);
129 self
130 }
131
132 pub fn env(mut self, env: HashMap<String, String>) -> Self {
134 self.env = Some(env);
135 self
136 }
137
138 pub fn executable_path(mut self, path: String) -> Self {
140 self.executable_path = Some(path);
141 self
142 }
143
144 pub fn firefox_user_prefs(mut self, prefs: HashMap<String, Value>) -> Self {
146 self.firefox_user_prefs = Some(prefs);
147 self
148 }
149
150 pub fn handle_sighup(mut self, enabled: bool) -> Self {
152 self.handle_sighup = Some(enabled);
153 self
154 }
155
156 pub fn handle_sigint(mut self, enabled: bool) -> Self {
158 self.handle_sigint = Some(enabled);
159 self
160 }
161
162 pub fn handle_sigterm(mut self, enabled: bool) -> Self {
164 self.handle_sigterm = Some(enabled);
165 self
166 }
167
168 pub fn headless(mut self, enabled: bool) -> Self {
170 self.headless = Some(enabled);
171 self
172 }
173
174 pub fn ignore_default_args(mut self, args: IgnoreDefaultArgs) -> Self {
176 self.ignore_default_args = Some(args);
177 self
178 }
179
180 pub fn proxy(mut self, proxy: ProxySettings) -> Self {
182 self.proxy = Some(proxy);
183 self
184 }
185
186 pub fn slow_mo(mut self, ms: f64) -> Self {
188 self.slow_mo = Some(ms);
189 self
190 }
191
192 pub fn timeout(mut self, ms: f64) -> Self {
194 self.timeout = Some(ms);
195 self
196 }
197
198 pub fn traces_dir(mut self, path: String) -> Self {
200 self.traces_dir = Some(path);
201 self
202 }
203
204 pub(crate) fn normalize(self) -> Value {
213 let mut value = serde_json::to_value(&self).unwrap();
214
215 if value.get("timeout").is_none() {
218 value["timeout"] = json!(crate::DEFAULT_TIMEOUT_MS);
219 }
220
221 if let Some(env_map) = value.get_mut("env") {
223 if let Some(map) = env_map.as_object() {
224 let env_array: Vec<_> = map
225 .iter()
226 .map(|(k, v)| json!({"name": k, "value": v}))
227 .collect();
228 *env_map = json!(env_array);
229 }
230 }
231
232 if let Some(ignore) = value.get("ignoreDefaultArgs") {
234 if let Some(b) = ignore.as_bool() {
235 if b {
236 value["ignoreAllDefaultArgs"] = json!(true);
237 }
238 value.as_object_mut().unwrap().remove("ignoreDefaultArgs");
239 }
240 }
241
242 value
243 }
244}
245
246#[cfg(test)]
247mod tests {
248 use super::*;
249
250 #[test]
251 fn test_launch_options_default() {
252 let opts = LaunchOptions::default();
253 assert!(opts.headless.is_none());
254 assert!(opts.args.is_none());
255 }
256
257 #[test]
258 fn test_launch_options_builder() {
259 let opts = LaunchOptions::default()
260 .headless(false)
261 .slow_mo(100.0)
262 .args(vec!["--no-sandbox".to_string()]);
263
264 assert_eq!(opts.headless, Some(false));
265 assert_eq!(opts.slow_mo, Some(100.0));
266 assert_eq!(opts.args, Some(vec!["--no-sandbox".to_string()]));
267 }
268
269 #[test]
270 fn test_launch_options_normalize_env() {
271 let opts = LaunchOptions::default().env(HashMap::from([
272 ("FOO".to_string(), "bar".to_string()),
273 ("BAZ".to_string(), "qux".to_string()),
274 ]));
275
276 let normalized = opts.normalize();
277
278 assert!(normalized["env"].is_array());
280 let env_array = normalized["env"].as_array().unwrap();
281 assert_eq!(env_array.len(), 2);
282
283 let names: Vec<_> = env_array
285 .iter()
286 .map(|v| v["name"].as_str().unwrap())
287 .collect();
288 assert!(names.contains(&"FOO"));
289 assert!(names.contains(&"BAZ"));
290 }
291
292 #[test]
293 fn test_launch_options_normalize_ignore_default_args_bool() {
294 let opts = LaunchOptions::default().ignore_default_args(IgnoreDefaultArgs::Bool(true));
295
296 let normalized = opts.normalize();
297
298 assert!(normalized["ignoreAllDefaultArgs"].as_bool().unwrap());
300 assert!(normalized.get("ignoreDefaultArgs").is_none());
301 }
302
303 #[test]
304 fn test_launch_options_normalize_ignore_default_args_array() {
305 let opts = LaunchOptions::default()
306 .ignore_default_args(IgnoreDefaultArgs::Array(vec!["--foo".to_string()]));
307
308 let normalized = opts.normalize();
309
310 assert!(normalized["ignoreDefaultArgs"].is_array());
312 assert_eq!(
313 normalized["ignoreDefaultArgs"][0].as_str().unwrap(),
314 "--foo"
315 );
316 }
317
318 #[test]
319 fn test_proxy_settings() {
320 let proxy = ProxySettings {
321 server: "http://proxy:8080".to_string(),
322 bypass: Some("localhost,127.0.0.1".to_string()),
323 username: Some("user".to_string()),
324 password: Some("pass".to_string()),
325 };
326
327 let opts = LaunchOptions::default().proxy(proxy);
328 assert!(opts.proxy.is_some());
329 }
330
331 #[test]
332 fn test_builder_pattern_chaining() {
333 let opts = LaunchOptions::new()
334 .headless(true)
335 .slow_mo(50.0)
336 .timeout(60000.0)
337 .args(vec![
338 "--no-sandbox".to_string(),
339 "--disable-gpu".to_string(),
340 ])
341 .channel("chrome".to_string());
342
343 assert_eq!(opts.headless, Some(true));
344 assert_eq!(opts.slow_mo, Some(50.0));
345 assert_eq!(opts.timeout, Some(60000.0));
346 assert_eq!(opts.args.as_ref().unwrap().len(), 2);
347 assert_eq!(opts.channel, Some("chrome".to_string()));
348 }
349}