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 =
214 serde_json::to_value(&self).expect("serialization of LaunchOptions cannot fail");
215
216 if value.get("timeout").is_none() {
219 value["timeout"] = json!(crate::DEFAULT_TIMEOUT_MS);
220 }
221
222 if let Some(env_map) = value.get_mut("env")
224 && let Some(map) = env_map.as_object()
225 {
226 let env_array: Vec<_> = map
227 .iter()
228 .map(|(k, v)| json!({"name": k, "value": v}))
229 .collect();
230 *env_map = json!(env_array);
231 }
232
233 if let Some(ignore) = value.get("ignoreDefaultArgs")
235 && let Some(b) = ignore.as_bool()
236 {
237 if b {
238 value["ignoreAllDefaultArgs"] = json!(true);
239 }
240 value
241 .as_object_mut()
242 .expect("params is a JSON object")
243 .remove("ignoreDefaultArgs");
244 }
245
246 value
247 }
248}
249
250#[cfg(test)]
251mod tests {
252 use super::*;
253
254 #[test]
255 fn test_launch_options_default() {
256 let opts = LaunchOptions::default();
257 assert!(opts.headless.is_none());
258 assert!(opts.args.is_none());
259 }
260
261 #[test]
262 fn test_launch_options_builder() {
263 let opts = LaunchOptions::default()
264 .headless(false)
265 .slow_mo(100.0)
266 .args(vec!["--no-sandbox".to_string()]);
267
268 assert_eq!(opts.headless, Some(false));
269 assert_eq!(opts.slow_mo, Some(100.0));
270 assert_eq!(opts.args, Some(vec!["--no-sandbox".to_string()]));
271 }
272
273 #[test]
274 fn test_launch_options_normalize_env() {
275 let opts = LaunchOptions::default().env(HashMap::from([
276 ("FOO".to_string(), "bar".to_string()),
277 ("BAZ".to_string(), "qux".to_string()),
278 ]));
279
280 let normalized = opts.normalize();
281
282 assert!(normalized["env"].is_array());
284 let env_array = normalized["env"].as_array().unwrap();
285 assert_eq!(env_array.len(), 2);
286
287 let names: Vec<_> = env_array
289 .iter()
290 .map(|v| v["name"].as_str().unwrap())
291 .collect();
292 assert!(names.contains(&"FOO"));
293 assert!(names.contains(&"BAZ"));
294 }
295
296 #[test]
297 fn test_launch_options_normalize_ignore_default_args_bool() {
298 let opts = LaunchOptions::default().ignore_default_args(IgnoreDefaultArgs::Bool(true));
299
300 let normalized = opts.normalize();
301
302 assert!(normalized["ignoreAllDefaultArgs"].as_bool().unwrap());
304 assert!(normalized.get("ignoreDefaultArgs").is_none());
305 }
306
307 #[test]
308 fn test_launch_options_normalize_ignore_default_args_array() {
309 let opts = LaunchOptions::default()
310 .ignore_default_args(IgnoreDefaultArgs::Array(vec!["--foo".to_string()]));
311
312 let normalized = opts.normalize();
313
314 assert!(normalized["ignoreDefaultArgs"].is_array());
316 assert_eq!(
317 normalized["ignoreDefaultArgs"][0].as_str().unwrap(),
318 "--foo"
319 );
320 }
321
322 #[test]
323 fn test_proxy_settings() {
324 let proxy = ProxySettings {
325 server: "http://proxy:8080".to_string(),
326 bypass: Some("localhost,127.0.0.1".to_string()),
327 username: Some("user".to_string()),
328 password: Some("pass".to_string()),
329 };
330
331 let opts = LaunchOptions::default().proxy(proxy);
332 assert!(opts.proxy.is_some());
333 }
334
335 #[test]
336 fn test_builder_pattern_chaining() {
337 let opts = LaunchOptions::new()
338 .headless(true)
339 .slow_mo(50.0)
340 .timeout(60000.0)
341 .args(vec![
342 "--no-sandbox".to_string(),
343 "--disable-gpu".to_string(),
344 ])
345 .channel("chrome".to_string());
346
347 assert_eq!(opts.headless, Some(true));
348 assert_eq!(opts.slow_mo, Some(50.0));
349 assert_eq!(opts.timeout, Some(60000.0));
350 assert_eq!(opts.args.as_ref().unwrap().len(), 2);
351 assert_eq!(opts.channel, Some("chrome".to_string()));
352 }
353}