playwright_rs/protocol/device.rs
1// Copyright 2026 Paul Adamson
2// Licensed under the Apache License, Version 2.0
3//
4// Device descriptor types for browser emulation.
5//
6// See: <https://playwright.dev/docs/emulation>
7
8use serde::Deserialize;
9
10/// Viewport dimensions for a device descriptor.
11#[derive(Clone, Debug, Deserialize)]
12pub struct DeviceViewport {
13 /// The viewport width in CSS pixels.
14 pub width: u32,
15 /// The viewport height in CSS pixels.
16 pub height: u32,
17}
18
19/// Describes a device for browser emulation.
20///
21/// Use with `BrowserContext::new_context()` options to emulate a specific device,
22/// matching the behavior of `playwright.devices["iPhone 13"]` in Python/JS.
23///
24/// Device descriptors are accessed by name from [`crate::protocol::Playwright::devices`].
25/// The name is the map key, not a field of the descriptor itself.
26///
27/// # Example
28///
29/// ```no_run
30/// use playwright_rs::protocol::Playwright;
31///
32/// #[tokio::main]
33/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
34/// let playwright = Playwright::launch().await?;
35///
36/// let iphone = &playwright.devices()["iPhone 13"];
37/// assert!(iphone.is_mobile);
38/// assert!(iphone.has_touch);
39/// assert_eq!(iphone.default_browser_type, "webkit");
40///
41/// playwright.shutdown().await?;
42/// Ok(())
43/// }
44/// ```
45///
46/// See: <https://playwright.dev/docs/api/class-playwright#playwright-devices>
47#[derive(Clone, Debug, Deserialize)]
48#[serde(rename_all = "camelCase")]
49#[non_exhaustive]
50pub struct DeviceDescriptor {
51 /// The user-agent string for the device.
52 pub user_agent: String,
53 /// The viewport dimensions.
54 pub viewport: DeviceViewport,
55 /// The screen size the device reports through `window.screen`, larger
56 /// than the viewport on a device whose browser chrome takes room. Pass
57 /// it as [`BrowserContextOptions::screen`](crate::protocol::BrowserContextOptions::screen)
58 /// to emulate the device fully. Not every descriptor has one.
59 pub screen: Option<DeviceViewport>,
60 /// The device pixel ratio (e.g., `3.0` for Retina displays).
61 pub device_scale_factor: f64,
62 /// Whether the device is a mobile device.
63 pub is_mobile: bool,
64 /// Whether the device supports touch input.
65 pub has_touch: bool,
66 /// The default browser type for the device: `"chromium"`, `"firefox"`, or `"webkit"`.
67 pub default_browser_type: String,
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 fn descriptor(screen: Option<serde_json::Value>) -> DeviceDescriptor {
75 let mut json = serde_json::json!({
76 "userAgent": "ua",
77 "viewport": {"width": 390, "height": 664},
78 "deviceScaleFactor": 3,
79 "isMobile": true,
80 "hasTouch": true,
81 "defaultBrowserType": "webkit"
82 });
83 if let Some(screen) = screen {
84 json["screen"] = screen;
85 }
86 serde_json::from_value(json).expect("descriptor deserializes")
87 }
88
89 #[test]
90 fn a_descriptor_carries_its_screen_when_it_has_one() {
91 let with = descriptor(Some(serde_json::json!({"width": 390, "height": 844})));
92 let screen = with.screen.expect("screen is read");
93 assert_eq!((screen.width, screen.height), (390, 844));
94 assert!(descriptor(None).screen.is_none());
95 }
96}