use std::borrow::Cow;
use anyhow::{Context, Result};
use async_trait::async_trait;
use chromiumoxide::types::MethodId;
use chromiumoxide::{Command, Method};
use serde::{Serialize, Serializer};
use serde_json::Value;
use crate::browser::chromiumoxide_adapter::ChromiumoxidePage;
use crate::fingerprint::apply::CdpTransport;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RawCdpCommand {
method: String,
params: Value,
}
impl RawCdpCommand {
pub fn new(method: impl Into<String>, params: Value) -> Self {
Self {
method: method.into(),
params,
}
}
}
impl Serialize for RawCdpCommand {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.params.serialize(serializer)
}
}
impl Method for RawCdpCommand {
fn identifier(&self) -> MethodId {
Cow::Owned(self.method.clone())
}
}
impl Command for RawCdpCommand {
type Response = Value;
}
#[async_trait]
impl CdpTransport for ChromiumoxidePage {
async fn send(&self, method: &str, params: Value) -> Result<Value> {
let response = self
.raw_page()
.execute(RawCdpCommand::new(method, params))
.await
.with_context(|| format!("{method} failed"))?;
Ok(response.result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn carries_the_method_name_out_of_band() {
let command = RawCdpCommand::new("Emulation.setLocaleOverride", json!({ "locale": "de" }));
assert_eq!(command.identifier(), "Emulation.setLocaleOverride");
assert_eq!(command.domain_name(), "Emulation");
}
#[test]
fn serializes_to_the_params_object_alone() {
let command = RawCdpCommand::new("Page.enable", json!({}));
assert_eq!(
serde_json::to_value(&command).expect("serialize"),
json!({})
);
}
#[test]
fn keeps_the_params_exactly_as_given() {
let params = json!({ "source": "globalThis.x = 1;", "nested": { "a": [1, 2] } });
let command = RawCdpCommand::new("Page.addScriptToEvaluateOnNewDocument", params.clone());
assert_eq!(serde_json::to_value(&command).expect("serialize"), params);
}
}