use std::sync::Arc;
use playwright_rs::protocol::Playwright;
use tokio::sync::Mutex;
use crate::test_server::TestServer;
#[tokio::test]
async fn test_request_headers() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
*captured.lock().await = Some(request);
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a request");
let headers = request.headers();
assert!(
!headers.is_empty(),
"Request headers should not be empty, got 0 headers"
);
let has_browser_header = headers.keys().any(|k| {
matches!(
k.as_str(),
"user-agent"
| "accept"
| "accept-encoding"
| "accept-language"
| "host"
| "upgrade-insecure-requests"
| "sec-ch-ua"
| "sec-ch-ua-mobile"
| "sec-ch-ua-platform"
)
});
assert!(
has_browser_header,
"Request headers should contain at least one standard browser header, got: {:?}",
headers.keys().collect::<Vec<_>>()
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_post_data_get() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a request");
let post_data = request.post_data();
assert!(
post_data.is_none(),
"GET request should have no post data, got: {:?}",
post_data
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_post_data_post() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
if request.method() == "POST" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
let post_url = format!("{}/echo-headers", server.url());
let _: serde_json::Value = page
.evaluate::<(), serde_json::Value>(
&format!(
r#"fetch('{}', {{
method: 'POST',
headers: {{'Content-Type': 'text/plain'}},
body: 'hello playwright'
}}).then(r => r.text())"#,
post_url
),
None,
)
.await
.expect("fetch should succeed");
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a POST request");
let post_data = request.post_data();
assert!(post_data.is_some(), "POST request should have post data");
let data = post_data.unwrap();
assert_eq!(
data, "hello playwright",
"Post data should match sent body, got: {:?}",
data
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_post_data_buffer() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
if request.method() == "POST" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
let post_url = format!("{}/echo-headers", server.url());
let _: serde_json::Value = page
.evaluate::<(), serde_json::Value>(
&format!(
r#"fetch('{}', {{
method: 'POST',
headers: {{'Content-Type': 'text/plain'}},
body: 'buffer test'
}}).then(r => r.text())"#,
post_url
),
None,
)
.await
.expect("fetch should succeed");
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a POST request");
let buf = request.post_data_buffer();
assert!(buf.is_some(), "POST request should have post data buffer");
let bytes = buf.unwrap();
assert_eq!(bytes, b"buffer test", "Buffer should match sent body bytes");
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_post_data_json() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
if request.method() == "POST" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
let post_url = format!("{}/echo-headers", server.url());
let _: serde_json::Value = page
.evaluate::<(), serde_json::Value>(
&format!(
r#"fetch('{}', {{
method: 'POST',
headers: {{'Content-Type': 'application/json'}},
body: JSON.stringify({{key: 'value', number: 42}})
}}).then(r => r.text())"#,
post_url
),
None,
)
.await
.expect("fetch should succeed");
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a POST request");
let json_result = request.post_data_json::<serde_json::Value>();
assert!(json_result.is_some(), "Should have JSON post data");
let value = json_result.unwrap().expect("JSON parse should succeed");
assert_eq!(
value.get("key").and_then(|v| v.as_str()),
Some("value"),
"Should parse key correctly"
);
assert_eq!(
value.get("number").and_then(|v| v.as_i64()),
Some(42),
"Should parse number correctly"
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_failure_none_for_success() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
let guard = captured.lock().await;
let request = guard.as_ref().expect("Should have captured a request");
let failure = request.failure();
assert!(
failure.is_none(),
"Successful request should have no failure, got: {:?}",
failure
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_failure_text() {
crate::common::init_tracing();
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request_failed(move |request| {
let captured = captured2.clone();
async move {
*captured.lock().await = Some(request);
Ok(())
}
})
.await
.expect("Failed to set request failed handler");
let _ = page.goto("http://localhost:1", None).await;
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
let guard = captured.lock().await;
let request = guard
.as_ref()
.expect("Should have captured a failed request");
let failure = request.failure();
assert!(failure.is_some(), "Failed request should have failure text");
let error_text = failure.unwrap();
assert!(
!error_text.is_empty(),
"Failure text should not be empty, got: {:?}",
error_text
);
browser.close().await.expect("Failed to close browser");
}
#[tokio::test]
async fn test_request_headers_array() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request_finished(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
let guard = captured.lock().await;
let request = guard
.as_ref()
.expect("Should have captured a finished request");
let headers_array = request
.headers_array()
.await
.expect("headers_array() should succeed");
assert!(
!headers_array.is_empty(),
"headers_array() should return at least one header"
);
for entry in &headers_array {
assert!(!entry.name.is_empty(), "Header name should not be empty");
}
let has_host = headers_array
.iter()
.any(|h| h.name.to_lowercase() == "host");
assert!(
has_host,
"headers_array() should include 'host' header, got: {:?}",
headers_array.iter().map(|h| &h.name).collect::<Vec<_>>()
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_all_headers() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request_finished(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
let guard = captured.lock().await;
let request = guard
.as_ref()
.expect("Should have captured a finished request");
let all_headers = request
.all_headers()
.await
.expect("all_headers() should succeed");
assert!(
!all_headers.is_empty(),
"all_headers() should return at least one header"
);
assert!(
all_headers.contains_key("host"),
"all_headers() should contain 'host' key (lowercased), got keys: {:?}",
all_headers.keys().collect::<Vec<_>>()
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_header_value() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request_finished(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
let guard = captured.lock().await;
let request = guard
.as_ref()
.expect("Should have captured a finished request");
let host = request
.header_value("host")
.await
.expect("header_value() should succeed");
assert!(host.is_some(), "header_value('host') should return a value");
let host_val = host.unwrap();
assert!(
!host_val.is_empty(),
"Host header value should not be empty"
);
let host_upper = request
.header_value("Host")
.await
.expect("header_value() should succeed with mixed case");
assert_eq!(
host_upper,
Some(host_val.clone()),
"header_value should be case-insensitive"
);
let missing = request
.header_value("x-does-not-exist")
.await
.expect("header_value() should succeed even for missing headers");
assert!(missing.is_none(), "Missing header should return None");
browser.close().await.expect("Failed to close browser");
server.shutdown();
}
#[tokio::test]
async fn test_request_timing() {
crate::common::init_tracing();
let server = TestServer::start().await;
let playwright = Playwright::launch()
.await
.expect("Failed to launch Playwright");
let browser = playwright
.chromium()
.launch()
.await
.expect("Failed to launch browser");
let page = browser.new_page().await.expect("Failed to create page");
let captured: Arc<Mutex<Option<playwright_rs::protocol::Request>>> = Arc::new(Mutex::new(None));
let captured2 = captured.clone();
page.on_request_finished(move |request| {
let captured = captured2.clone();
async move {
if request.resource_type() == "document" {
*captured.lock().await = Some(request);
}
Ok(())
}
})
.await
.expect("Failed to set request handler");
page.goto(&server.url(), None)
.await
.expect("Failed to navigate");
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
let guard = captured.lock().await;
let request = guard
.as_ref()
.expect("Should have captured a finished request");
let timing = request.timing().await.expect("timing() should succeed");
assert!(
timing.start_time >= 0.0,
"start_time should be non-negative, got: {}",
timing.start_time
);
assert!(
timing.response_start >= -1.0,
"response_start should be >= -1 (Playwright uses -1 for unavailable), got: {}",
timing.response_start
);
browser.close().await.expect("Failed to close browser");
server.shutdown();
}