Skip to main content

playwright_cdp/
options.rs

1//! Builder option structs mirroring Playwright's option objects.
2
3use crate::types::{
4    KeyboardModifier, MouseButton, Position, ProxySettings, ScreenshotClip, ScreenshotType,
5    Viewport, DEFAULT_TIMEOUT_MS,
6};
7use std::collections::HashMap;
8use std::time::Duration;
9
10/// Browser launch options. See: <https://playwright.dev/docs/api/class-browsertype#browser-type-launch>
11#[derive(Debug, Clone, Default)]
12#[non_exhaustive]
13pub struct LaunchOptions {
14    pub headless: Option<bool>,
15    pub executable_path: Option<String>,
16    pub channel: Option<String>,
17    pub args: Option<Vec<String>>,
18    pub env: Option<HashMap<String, String>>,
19    pub proxy: Option<ProxySettings>,
20    pub slow_mo: Option<f64>,
21    pub timeout: Option<f64>,
22    pub downloads_path: Option<String>,
23    pub traces_dir: Option<String>,
24    pub devtools: Option<bool>,
25    pub chromium_sandbox: Option<bool>,
26    /// Optional persistent user-data-dir. When set, the browser is launched
27    /// against this directory instead of a throwaway temp dir, so cookies,
28    /// localStorage, etc. persist across runs (Playwright `userDataDir`).
29    pub user_data_dir: Option<std::path::PathBuf>,
30}
31
32impl LaunchOptions {
33    pub fn new() -> Self {
34        Self::default()
35    }
36    pub fn headless(mut self, v: bool) -> Self {
37        self.headless = Some(v);
38        self
39    }
40    pub fn executable_path(mut self, v: impl Into<String>) -> Self {
41        self.executable_path = Some(v.into());
42        self
43    }
44    pub fn channel(mut self, v: impl Into<String>) -> Self {
45        self.channel = Some(v.into());
46        self
47    }
48    pub fn args(mut self, v: Vec<String>) -> Self {
49        self.args = Some(v);
50        self
51    }
52    pub fn proxy(mut self, v: ProxySettings) -> Self {
53        self.proxy = Some(v);
54        self
55    }
56    pub fn timeout_ms(mut self, v: f64) -> Self {
57        self.timeout = Some(v);
58        self
59    }
60    pub fn devtools(mut self, v: bool) -> Self {
61        self.devtools = Some(v);
62        self
63    }
64    /// Set a persistent user-data-dir (Playwright `userDataDir`).
65    pub fn user_data_dir(mut self, v: impl Into<std::path::PathBuf>) -> Self {
66        self.user_data_dir = Some(v.into());
67        self
68    }
69
70    /// Resolved action/launch timeout in milliseconds.
71    pub fn timeout_ms_or_default(&self) -> f64 {
72        self.timeout.unwrap_or(DEFAULT_TIMEOUT_MS)
73    }
74}
75
76/// Options for `connect_over_cdp`.
77#[derive(Debug, Clone, Default)]
78#[non_exhaustive]
79pub struct ConnectOverCdpOptions {
80    pub headers: Option<HashMap<String, String>>,
81    pub slow_mo: Option<f64>,
82    pub timeout: Option<f64>,
83    pub no_defaults: Option<bool>,
84}
85
86impl ConnectOverCdpOptions {
87    pub fn new() -> Self {
88        Self::default()
89    }
90    pub fn headers(mut self, v: HashMap<String, String>) -> Self {
91        self.headers = Some(v);
92        self
93    }
94    pub fn timeout_ms(mut self, v: f64) -> Self {
95        self.timeout = Some(v);
96        self
97    }
98}
99
100/// When to consider a navigation finished.
101#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
102pub enum WaitUntil {
103    /// Consider navigation complete when the `load` event fires.
104    #[default]
105    Load,
106    /// `DOMContentLoaded`.
107    DomContentLoaded,
108    /// Heuristic: no in-flight network requests for ~500ms after `load`.
109    NetworkIdle,
110    /// Resolved as soon as the navigation is committed.
111    Commit,
112}
113
114impl WaitUntil {
115    pub fn as_str(&self) -> &'static str {
116        match self {
117            WaitUntil::Load => "load",
118            WaitUntil::DomContentLoaded => "DOMContentLoaded",
119            WaitUntil::NetworkIdle => "networkidle",
120            WaitUntil::Commit => "commit",
121        }
122    }
123}
124
125/// Options for navigation methods (`goto`, `reload`, ...).
126#[derive(Debug, Clone, Default)]
127#[non_exhaustive]
128pub struct GotoOptions {
129    pub timeout: Option<Duration>,
130    pub wait_until: Option<WaitUntil>,
131    pub referer: Option<String>,
132}
133
134impl GotoOptions {
135    pub fn new() -> Self {
136        Self::default()
137    }
138    pub fn timeout(mut self, v: Duration) -> Self {
139        self.timeout = Some(v);
140        self
141    }
142    pub fn wait_until(mut self, v: WaitUntil) -> Self {
143        self.wait_until = Some(v);
144        self
145    }
146    pub fn referer(mut self, v: impl Into<String>) -> Self {
147        self.referer = Some(v.into());
148        self
149    }
150    pub fn wait_until_or_default(&self) -> WaitUntil {
151        self.wait_until.unwrap_or_default()
152    }
153}
154
155/// Options for `Locator::click` / `dblclick`.
156#[derive(Debug, Clone, Default)]
157#[non_exhaustive]
158pub struct ClickOptions {
159    pub button: Option<MouseButton>,
160    pub click_count: Option<u32>,
161    pub delay: Option<f64>,
162    pub force: Option<bool>,
163    pub modifiers: Option<Vec<KeyboardModifier>>,
164    pub no_wait_after: Option<bool>,
165    pub position: Option<Position>,
166    pub timeout: Option<f64>,
167    pub trial: Option<bool>,
168}
169
170impl ClickOptions {
171    pub fn new() -> Self {
172        Self::default()
173    }
174    pub fn timeout_ms(mut self, v: f64) -> Self {
175        self.timeout = Some(v);
176        self
177    }
178    pub fn force(mut self, v: bool) -> Self {
179        self.force = Some(v);
180        self
181    }
182}
183
184/// Options for `Locator::fill` / `clear`.
185#[derive(Debug, Clone, Default)]
186#[non_exhaustive]
187pub struct FillOptions {
188    pub force: Option<bool>,
189    pub timeout: Option<f64>,
190}
191
192impl FillOptions {
193    pub fn new() -> Self {
194        Self::default()
195    }
196    pub fn timeout_ms(mut self, v: f64) -> Self {
197        self.timeout = Some(v);
198        self
199    }
200}
201
202/// Options for `Locator::press`.
203#[derive(Debug, Clone, Default)]
204#[non_exhaustive]
205pub struct PressOptions {
206    pub delay: Option<f64>,
207    pub timeout: Option<f64>,
208}
209
210/// Options for `Keyboard::type_` (sequential key presses).
211#[derive(Debug, Clone, Default)]
212#[non_exhaustive]
213pub struct PressSequentiallyOptions {
214    pub delay: Option<f64>,
215    pub timeout: Option<f64>,
216}
217
218/// Options for `Mouse::click` / `dblclick` / `down` / `up` / `move_to`.
219#[derive(Debug, Clone, Default)]
220#[non_exhaustive]
221pub struct MouseOptions {
222    pub button: Option<MouseButton>,
223    pub click_count: Option<u32>,
224    pub delay: Option<f64>,
225}
226
227/// Options for `Locator::hover`.
228#[derive(Debug, Clone, Default)]
229#[non_exhaustive]
230pub struct HoverOptions {
231    pub force: Option<bool>,
232    pub modifiers: Option<Vec<KeyboardModifier>>,
233    pub no_wait_after: Option<bool>,
234    pub position: Option<Position>,
235    pub timeout: Option<f64>,
236    pub trial: Option<bool>,
237}
238
239/// Options for `Locator::wait_for`.
240#[derive(Debug, Clone, Default)]
241#[non_exhaustive]
242pub struct WaitForOptions {
243    pub state: Option<WaitForState>,
244    pub timeout: Option<f64>,
245}
246
247/// Element state to wait for.
248#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
249pub enum WaitForState {
250    #[default]
251    Visible,
252    Hidden,
253    Attached,
254    Detached,
255}
256
257/// Options for `Page::screenshot` / `Locator::screenshot`.
258#[derive(Debug, Clone, Default)]
259#[non_exhaustive]
260pub struct ScreenshotOptions {
261    pub path: Option<String>,
262    pub r#type: Option<ScreenshotType>,
263    pub quality: Option<u8>,
264    pub full_page: Option<bool>,
265    pub clip: Option<ScreenshotClip>,
266    pub omit_background: Option<bool>,
267    pub timeout: Option<f64>,
268}
269
270impl ScreenshotOptions {
271    pub fn new() -> Self {
272        Self::default()
273    }
274    pub fn path(mut self, v: impl Into<String>) -> Self {
275        self.path = Some(v.into());
276        self
277    }
278    pub fn full_page(mut self, v: bool) -> Self {
279        self.full_page = Some(v);
280        self
281    }
282    pub fn r#type(mut self, v: ScreenshotType) -> Self {
283        self.r#type = Some(v);
284        self
285    }
286    pub fn omit_background(mut self, v: bool) -> Self {
287        self.omit_background = Some(v);
288        self
289    }
290}
291
292/// Options for `get_by_role`.
293#[derive(Debug, Clone, Default)]
294#[non_exhaustive]
295pub struct GetByRoleOptions {
296    pub checked: Option<bool>,
297    pub disabled: Option<bool>,
298    pub selected: Option<bool>,
299    pub expanded: Option<bool>,
300    pub include_hidden: Option<bool>,
301    pub level: Option<u32>,
302    pub name: Option<String>,
303    pub description: Option<String>,
304    pub exact: Option<bool>,
305    pub pressed: Option<bool>,
306}
307
308impl GetByRoleOptions {
309    pub fn new() -> Self {
310        Self::default()
311    }
312    pub fn name(mut self, v: impl Into<String>) -> Self {
313        self.name = Some(v.into());
314        self
315    }
316    pub fn exact(mut self, v: bool) -> Self {
317        self.exact = Some(v);
318        self
319    }
320}
321
322/// Whether a new page should be opened with a specific viewport.
323#[derive(Debug, Clone, Default)]
324#[non_exhaustive]
325pub struct NewContextOptions {
326    pub viewport: Option<Viewport>,
327    pub user_agent: Option<String>,
328    pub locale: Option<String>,
329    pub extra_http_headers: Option<HashMap<String, String>>,
330    pub ignore_https_errors: Option<bool>,
331}
332
333/// A selection criterion for `Locator::select_option`.
334#[derive(Debug, Clone)]
335#[non_exhaustive]
336pub enum SelectOption {
337    /// Match by the option's `value` attribute.
338    Value(String),
339    /// Match by the option's visible label text.
340    Label(String),
341    /// Match by index (0-based).
342    Index(u32),
343}
344
345impl SelectOption {
346    pub fn value(v: impl Into<String>) -> Self {
347        Self::Value(v.into())
348    }
349    pub fn label(v: impl Into<String>) -> Self {
350        Self::Label(v.into())
351    }
352}
353
354/// Options for `Locator::check` / `uncheck` / `set_checked`.
355#[derive(Debug, Clone, Default)]
356#[non_exhaustive]
357pub struct CheckOptions {
358    pub force: Option<bool>,
359    pub position: Option<Position>,
360    pub timeout: Option<f64>,
361    pub trial: Option<bool>,
362}
363
364/// Options for `Locator::select_option`.
365#[derive(Debug, Clone, Default)]
366#[non_exhaustive]
367pub struct SelectOptions {
368    pub force: Option<bool>,
369    pub timeout: Option<f64>,
370}
371
372/// Options for `Locator::filter` (minimal).
373#[derive(Debug, Clone, Default)]
374#[non_exhaustive]
375pub struct FilterOptions {
376    pub has_text: Option<String>,
377    pub has_not_text: Option<String>,
378}
379
380/// Emulated media (for `Page::emulate_media`).
381#[derive(Debug, Clone, Default)]
382#[non_exhaustive]
383pub struct EmulateMediaOptions {
384    pub media: Option<Media>,
385    pub color_scheme: Option<ColorScheme>,
386    pub reduced_motion: Option<ReducedMotion>,
387}
388
389/// Emulated `prefers-media` value.
390#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
391pub enum Media {
392    #[default]
393    Screen,
394    Print,
395}
396
397/// Emulated `prefers-color-scheme` value.
398#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
399pub enum ColorScheme {
400    Light,
401    Dark,
402    #[default]
403    NoPreference,
404}
405
406/// Emulated `prefers-reduced-motion` value.
407#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
408pub enum ReducedMotion {
409    #[default]
410    NoPreference,
411    Reduce,
412}
413
414/// Options for `drag_to` / `drag_and_drop` (minimal).
415#[derive(Debug, Clone, Default)]
416#[non_exhaustive]
417pub struct DragToOptions {
418    pub force: Option<bool>,
419    pub timeout: Option<f64>,
420}
421
422/// Options for `Page::pdf` (minimal subset of CDP `Page.printToPDF`).
423#[derive(Debug, Clone, Default)]
424#[non_exhaustive]
425pub struct PdfOptions {
426    pub format: Option<PaperFormat>,
427    pub landscape: Option<bool>,
428    pub print_background: Option<bool>,
429    pub scale: Option<f64>,
430    pub margin: Option<PdfMargin>,
431    pub prefer_css_page_size: Option<bool>,
432}
433
434impl PdfOptions {
435    pub fn new() -> Self {
436        Self::default()
437    }
438    pub fn format(mut self, v: PaperFormat) -> Self {
439        self.format = Some(v);
440        self
441    }
442    pub fn landscape(mut self, v: bool) -> Self {
443        self.landscape = Some(v);
444        self
445    }
446}
447
448/// Standard paper sizes.
449#[derive(Debug, Clone, Copy, PartialEq)]
450pub enum PaperFormat {
451    Letter,
452    Legal,
453    Tabloid,
454    Ledger,
455    A0,
456    A1,
457    A2,
458    A3,
459    A4,
460    A5,
461    A6,
462}
463
464impl PaperFormat {
465    /// `(width, height)` in inches.
466    pub fn inches(self) -> (f64, f64) {
467        match self {
468            PaperFormat::Letter => (8.5, 11.0),
469            PaperFormat::Legal => (8.5, 14.0),
470            PaperFormat::Tabloid => (11.0, 17.0),
471            PaperFormat::Ledger => (17.0, 11.0),
472            PaperFormat::A0 => (33.1, 46.8),
473            PaperFormat::A1 => (23.4, 33.1),
474            PaperFormat::A2 => (16.54, 23.4),
475            PaperFormat::A3 => (11.7, 16.54),
476            PaperFormat::A4 => (8.27, 11.69),
477            PaperFormat::A5 => (5.83, 8.27),
478            PaperFormat::A6 => (4.13, 5.83),
479        }
480    }
481}
482
483/// PDF page margins in inches.
484#[derive(Debug, Clone, Copy, Default)]
485#[non_exhaustive]
486pub struct PdfMargin {
487    pub top: Option<f64>,
488    pub bottom: Option<f64>,
489    pub left: Option<f64>,
490    pub right: Option<f64>,
491}
492
493/// Options for [`Tracing::start`](crate::Tracing::start).
494///
495/// Minimal subset of Playwright's `tracing.start()` options. Captures a Chrome
496/// performance trace via the CDP `Tracing` domain (browser-level).
497#[derive(Debug, Clone, Default)]
498#[non_exhaustive]
499pub struct TracingStartOptions {
500    /// Trace categories to enable (CDP `Tracing.start` `categories`). If
501    /// omitted, Chrome's defaults are used.
502    pub categories: Option<Vec<String>>,
503    /// Where to write the trace JSON file once [`Tracing::stop`] is called.
504    pub path: Option<String>,
505    /// When true, captures screenshots (adds the
506    /// `disabled-by-default-devtools.screenshot` category).
507    pub screenshots: Option<bool>,
508}
509
510impl TracingStartOptions {
511    pub fn new() -> Self {
512        Self::default()
513    }
514    pub fn categories(mut self, v: Vec<String>) -> Self {
515        self.categories = Some(v);
516        self
517    }
518    pub fn path(mut self, v: impl Into<String>) -> Self {
519        self.path = Some(v.into());
520        self
521    }
522    pub fn screenshots(mut self, v: bool) -> Self {
523        self.screenshots = Some(v);
524        self
525    }
526}
527
528/// Options for `Page::wait_for_function` / `Frame::wait_for_function`.
529///
530/// Polls `expression` until it yields a truthy value (or `timeout` elapses).
531/// See: <https://playwright.dev/docs/api/class-page#page-wait-for-function>
532#[derive(Debug, Clone, Default)]
533#[non_exhaustive]
534pub struct WaitForFunctionOptions {
535    /// Maximum time to wait, in milliseconds. Defaults to the page's default
536    /// timeout (30s) when `None`.
537    pub timeout: Option<f64>,
538    /// Time to wait between evaluations, in milliseconds. Defaults to 100ms.
539    pub polling_interval: Option<f64>,
540}
541
542impl WaitForFunctionOptions {
543    pub fn new() -> Self {
544        Self::default()
545    }
546    /// Set the maximum wait time in milliseconds.
547    pub fn timeout_ms(mut self, v: f64) -> Self {
548        self.timeout = Some(v);
549        self
550    }
551    /// Set the time between polls in milliseconds (default 100).
552    pub fn polling_interval_ms(mut self, v: f64) -> Self {
553        self.polling_interval = Some(v);
554        self
555    }
556}
557
558/// Options for [`crate::APIRequestContext`] requests.
559///
560/// Mirrors the relevant subset of Playwright's API request options. `data`
561/// (JSON body) and `form` (URL-encoded form) are mutually exclusive; if both
562/// are set, `data` wins.
563///
564/// See: <https://playwright.dev/docs/api/class-apirequestcontext#api-request-context-get>
565#[derive(Debug, Clone, Default)]
566#[non_exhaustive]
567pub struct APIRequestOptions {
568    /// Per-request headers (merged on top of the context's default headers).
569    pub headers: Option<HashMap<String, String>>,
570    /// URL query parameters.
571    pub params: Option<Vec<(String, String)>>,
572    /// JSON request body. Sent as `application/json`.
573    pub data: Option<serde_json::Value>,
574    /// URL-encoded form body. Sent as `application/x-www-form-urlencoded`.
575    pub form: Option<HashMap<String, String>>,
576    /// Per-request timeout, in milliseconds.
577    pub timeout: Option<f64>,
578}
579
580impl APIRequestOptions {
581    pub fn new() -> Self {
582        Self::default()
583    }
584
585    /// Set the per-request headers (replaces any previously set headers).
586    pub fn headers(mut self, v: HashMap<String, String>) -> Self {
587        self.headers = Some(v);
588        self
589    }
590
591    /// Add a single header.
592    pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
593        self.headers
594            .get_or_insert_with(HashMap::new)
595            .insert(name.into(), value.into());
596        self
597    }
598
599    /// Set the JSON body.
600    pub fn data(mut self, v: serde_json::Value) -> Self {
601        self.data = Some(v);
602        self
603    }
604
605    /// Set the URL-encoded form body.
606    pub fn form(mut self, v: HashMap<String, String>) -> Self {
607        self.form = Some(v);
608        self
609    }
610
611    /// Set the URL query parameters.
612    pub fn params(mut self, v: Vec<(String, String)>) -> Self {
613        self.params = Some(v);
614        self
615    }
616
617    /// Set the per-request timeout in milliseconds.
618    pub fn timeout_ms(mut self, v: f64) -> Self {
619        self.timeout = Some(v);
620        self
621    }
622}