use serde::{Deserialize, Serialize};
use std::fmt;
pub use crate::api::page::Viewport;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ScreenshotFormat {
Jpeg,
Png,
Webp,
}
impl ScreenshotFormat {
pub fn as_cdp_str(self) -> &'static str {
match self {
Self::Jpeg => "jpeg",
Self::Png => "png",
Self::Webp => "webp",
}
}
pub fn from_cdp(s: Option<&str>) -> Self {
match s {
Some("jpeg") => Self::Jpeg,
Some("webp") => Self::Webp,
_ => Self::Png,
}
}
}
impl Default for ScreenshotFormat {
fn default() -> Self {
Self::Png
}
}
impl fmt::Display for ScreenshotFormat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_cdp_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WaitUntilState {
Load,
DomContentLoaded,
NetworkIdle0,
NetworkIdle2,
}
impl WaitUntilState {
pub fn as_str(self) -> &'static str {
match self {
Self::Load => "load",
Self::DomContentLoaded => "domcontentloaded",
Self::NetworkIdle0 => "networkidle0",
Self::NetworkIdle2 => "networkidle2",
}
}
}
impl Default for WaitUntilState {
fn default() -> Self {
Self::Load
}
}
impl fmt::Display for WaitUntilState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
pub struct Cookie {
pub name: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub http_only: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub secure: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub same_site: Option<String>,
}
impl Cookie {
pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
Self {
name: name.into(),
value: value.into(),
..Default::default()
}
}
pub fn with_domain(mut self, domain: impl Into<String>) -> Self {
self.domain = Some(domain.into());
self
}
pub fn with_path(mut self, path: impl Into<String>) -> Self {
self.path = Some(path.into());
self
}
pub fn with_secure(mut self, secure: bool) -> Self {
self.secure = Some(secure);
self
}
pub fn with_http_only(mut self, http_only: bool) -> Self {
self.http_only = Some(http_only);
self
}
pub fn with_same_site(mut self, same_site: impl Into<String>) -> Self {
self.same_site = Some(same_site.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeviceDescriptor {
pub name: String,
pub user_agent: String,
pub viewport: Viewport,
}
impl DeviceDescriptor {
pub fn new(name: impl Into<String>, user_agent: impl Into<String>, viewport: Viewport) -> Self {
Self {
name: name.into(),
user_agent: user_agent.into(),
viewport,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn screenshot_format_default_is_png() {
assert_eq!(ScreenshotFormat::default(), ScreenshotFormat::Png);
}
#[test]
fn screenshot_format_round_trip() {
for fmt in [
ScreenshotFormat::Png,
ScreenshotFormat::Jpeg,
ScreenshotFormat::Webp,
] {
let s = fmt.as_cdp_str();
assert_eq!(ScreenshotFormat::from_cdp(Some(s)), fmt);
}
}
#[test]
fn screenshot_format_unknown_falls_back_to_png() {
assert_eq!(
ScreenshotFormat::from_cdp(Some("gif")),
ScreenshotFormat::Png
);
assert_eq!(ScreenshotFormat::from_cdp(None), ScreenshotFormat::Png);
}
#[test]
fn screenshot_format_display_matches_cdp_str() {
assert_eq!(format!("{}", ScreenshotFormat::Webp), "webp");
}
#[test]
fn wait_until_default_is_load() {
assert_eq!(WaitUntilState::default(), WaitUntilState::Load);
}
#[test]
fn wait_until_as_str_all_variants() {
assert_eq!(WaitUntilState::Load.as_str(), "load");
assert_eq!(
WaitUntilState::DomContentLoaded.as_str(),
"domcontentloaded"
);
assert_eq!(WaitUntilState::NetworkIdle0.as_str(), "networkidle0");
assert_eq!(WaitUntilState::NetworkIdle2.as_str(), "networkidle2");
}
#[test]
fn cookie_default_all_optional_none() {
let c = Cookie {
name: "k".into(),
value: "v".into(),
..Default::default()
};
assert!(c.url.is_none());
assert!(c.domain.is_none());
assert!(c.path.is_none());
assert!(c.expires.is_none());
assert!(c.http_only.is_none());
assert!(c.secure.is_none());
assert!(c.same_site.is_none());
}
#[test]
fn cookie_builder_chain() {
let c = Cookie::new("k", "v")
.with_domain("example.com")
.with_path("/")
.with_secure(true)
.with_http_only(true)
.with_same_site("Lax");
assert_eq!(c.domain.as_deref(), Some("example.com"));
assert_eq!(c.path.as_deref(), Some("/"));
assert_eq!(c.secure, Some(true));
assert_eq!(c.http_only, Some(true));
assert_eq!(c.same_site.as_deref(), Some("Lax"));
}
#[test]
fn cookie_serializes_with_only_required_fields() {
let c = Cookie::new("k", "v");
let json = serde_json::to_string(&c).unwrap();
assert!(json.contains("\"name\":\"k\""));
assert!(json.contains("\"value\":\"v\""));
assert!(!json.contains("domain"));
}
#[test]
fn cookie_deserializes_back() {
let c = Cookie::new("k", "v").with_domain("example.com");
let json = serde_json::to_string(&c).unwrap();
let parsed: Cookie = serde_json::from_str(&json).unwrap();
assert_eq!(c, parsed);
}
#[test]
fn device_descriptor_construction() {
let vp = Viewport {
width: 390,
height: 844,
device_scale_factor: 3.0,
is_mobile: true,
has_touch: true,
is_landscape: false,
};
let dev = DeviceDescriptor::new("iPhone 13", "UA", vp);
assert_eq!(dev.name, "iPhone 13");
assert_eq!(dev.user_agent, "UA");
assert_eq!(dev.viewport.width, 390);
assert!(dev.viewport.is_mobile);
}
}