use std::time::Duration;
use playwright_cdp::Playwright;
async fn http_server(body: &'static str) -> String {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
tokio::spawn(async move {
loop {
let Ok((mut sock, _)) = listener.accept().await else { break };
let body = body;
tokio::spawn(async move {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut buf = [0u8; 4096];
let _ = sock.read(&mut buf).await;
let resp = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
body.len(),
body
);
let _ = sock.write_all(resp.as_bytes()).await;
let _ = sock.flush().await;
});
}
});
format!("http://127.0.0.1:{port}/")
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let _ = tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "playwright_cdp=info".into()),
)
.try_init();
let base = http_server("<html><body><p>persistent-context demo</p></body></html>").await;
println!("embedded server origin: {base}");
let dir = std::env::temp_dir().join("pw-cdp-persistent-demo");
let _ = std::fs::remove_dir_all(&dir);
println!("user-data-dir: {}", dir.display());
println!("\n[session 1] launching persistent context...");
{
let ctx = Playwright::launch()
.await?
.chromium()
.launch_persistent_context(&dir)
.await?;
let page = ctx.new_page().await?;
page.goto(&base, None).await?;
let _: serde_json::Value = page
.evaluate("localStorage.setItem('k','persisted')")
.await?;
let val: Option<String> = page.evaluate("localStorage.getItem('k')").await?;
println!("[session 1] wrote localStorage['k'] = {val:?}");
assert_eq!(val.as_deref(), Some("persisted"));
ctx.close().await?;
println!("[session 1] closed (user-data-dir stays on disk)");
}
assert!(dir.exists(), "user-data-dir must persist after close");
println!("\n[session 2] re-launching persistent context against the same dir...");
{
let ctx = Playwright::launch()
.await?
.chromium()
.launch_persistent_context(&dir)
.await?;
let page = ctx.new_page().await?;
page.goto(&base, None).await?;
tokio::time::sleep(Duration::from_millis(100)).await;
let val: Option<String> = page
.evaluate("localStorage.getItem('k')")
.await?;
println!("[session 2] read localStorage['k'] = {val:?}");
assert_eq!(
val.as_deref(),
Some("persisted"),
"localStorage must persist across sessions via the user-data-dir"
);
ctx.close().await?;
println!("[session 2] closed");
}
let _ = std::fs::remove_dir_all(&dir);
println!("\npersistent storage survived across two browser sessions.");
Ok(())
}