use playwright_rs::protocol::JSHandle;
use playwright_rs::server::channel_owner::ChannelOwner;
use std::sync::{Arc, Mutex};
#[tokio::test]
async fn test_page_on_console_log() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let captured = Arc::new(Mutex::new(None));
let captured_clone = captured.clone();
page.on_console(move |msg| {
let cap = captured_clone.clone();
async move {
*cap.lock().unwrap() = Some((msg.type_().to_string(), msg.text().to_string()));
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('hello')").await?;
waiter.wait().await.expect("console event did not fire");
let result = captured.lock().unwrap().take();
assert!(result.is_some(), "on_console handler should have fired");
let (type_, text) = result.unwrap();
assert_eq!(type_, "log", "Console message type should be 'log'");
assert_eq!(text, "hello", "Console message text should be 'hello'");
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_page_on_console_error() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let captured_type = Arc::new(Mutex::new(None));
let captured_clone = captured_type.clone();
page.on_console(move |msg| {
let cap = captured_clone.clone();
async move {
*cap.lock().unwrap() = Some(msg.type_().to_string());
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.error('oops')").await?;
waiter.wait().await.expect("console event did not fire");
let result = captured_type.lock().unwrap().take();
assert!(
result.is_some(),
"on_console handler should have fired for error"
);
assert_eq!(
result.unwrap(),
"error",
"Console message type should be 'error'"
);
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_page_on_console_location() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let captured_url = Arc::new(Mutex::new(None));
let captured_line = Arc::new(Mutex::new(None));
let url_clone = captured_url.clone();
let line_clone = captured_line.clone();
page.on_console(move |msg| {
let url_cap = url_clone.clone();
let line_cap = line_clone.clone();
async move {
let loc = msg.location();
*url_cap.lock().unwrap() = Some(loc.url.clone());
*line_cap.lock().unwrap() = Some(loc.line_number);
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('location test')")
.await?;
waiter.wait().await.expect("console event did not fire");
let url = captured_url.lock().unwrap().take();
let line = captured_line.lock().unwrap().take();
assert!(url.is_some(), "Location URL should be populated");
assert!(line.is_some(), "Location line_number should be populated");
let _ = url.unwrap(); let _ = line.unwrap();
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_console_message_page_back_reference() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let page_guid_captured = Arc::new(Mutex::new(None));
let cap_clone = page_guid_captured.clone();
let page_guid = page.guid().to_string();
page.on_console(move |msg| {
let cap = cap_clone.clone();
async move {
let back_page = msg.page();
*cap.lock().unwrap() = back_page.map(|p| p.guid().to_string());
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('back ref test')")
.await?;
waiter.wait().await.expect("console event did not fire");
let result = page_guid_captured.lock().unwrap().take();
assert!(result.is_some(), "msg.page() should return Some(page)");
assert_eq!(
result.unwrap(),
page_guid,
"msg.page() should return the page that triggered the console event"
);
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_context_on_console() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, _browser, context) = crate::common::setup_context().await;
let page = context.new_page().await?;
let captured = Arc::new(Mutex::new(None));
let cap_clone = captured.clone();
context
.on_console(move |msg| {
let cap = cap_clone.clone();
async move {
*cap.lock().unwrap() = Some((msg.type_().to_string(), msg.text().to_string()));
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = context.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('context console test')")
.await?;
waiter.wait().await.expect("console event did not fire");
let result = captured.lock().unwrap().take();
assert!(
result.is_some(),
"context.on_console handler should have fired"
);
let (type_, text) = result.unwrap();
assert_eq!(type_, "log");
assert_eq!(text, "context console test");
context.close().await?;
Ok(())
}
#[tokio::test]
async fn test_console_message_args() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let captured_args: Arc<Mutex<Option<Vec<Arc<JSHandle>>>>> = Arc::new(Mutex::new(None));
let cap_clone = captured_args.clone();
page.on_console(move |msg| {
let cap = cap_clone.clone();
async move {
*cap.lock().unwrap() = Some(msg.args().to_vec());
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('hello', 42)").await?;
waiter.wait().await.expect("console event did not fire");
let args = captured_args.lock().unwrap().take();
assert!(args.is_some(), "on_console handler should have fired");
let args = args.unwrap();
assert_eq!(
args.len(),
2,
"console.log('hello', 42) should produce 2 args"
);
let first = args[0].json_value().await?;
assert_eq!(
first,
serde_json::json!("hello"),
"First arg should be 'hello'"
);
let second = args[1].json_value().await?;
assert_eq!(second, serde_json::json!(42), "Second arg should be 42");
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_page_console_messages() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let _ = page.goto("about:blank", None).await;
let waiter_a = page.expect_console_message(Some(5000.0)).await?;
let waiter_b = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('a')").await?;
page.evaluate_expression("console.log('b')").await?;
waiter_a
.wait()
.await
.expect("first console event did not fire");
waiter_b
.wait()
.await
.expect("second console event did not fire");
let msgs = page.console_messages();
assert!(
msgs.len() >= 2,
"Expected at least 2 console messages, got {}",
msgs.len()
);
let texts: Vec<&str> = msgs.iter().map(|m| m.text()).collect();
assert!(
texts.contains(&"a"),
"console_messages() should contain 'a', got: {:?}",
texts
);
assert!(
texts.contains(&"b"),
"console_messages() should contain 'b', got: {:?}",
texts
);
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_page_page_errors() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_event("pageerror", Some(5000.0)).await?;
page.evaluate::<(), ()>(
"() => { setTimeout(() => { throw new Error('test-uncaught'); }, 0); }",
None,
)
.await?;
waiter.wait().await.expect("pageerror event did not fire");
let errors = page.page_errors();
assert!(
!errors.is_empty(),
"page_errors() should have at least one entry after a thrown error"
);
assert!(
errors.iter().any(|e| e.contains("test-uncaught")),
"page_errors() should contain 'test-uncaught', got: {:?}",
errors
);
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_console_message_timestamp() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, browser, page) = crate::common::setup().await;
let captured_ts: Arc<Mutex<Option<f64>>> = Arc::new(Mutex::new(None));
let cap_clone = captured_ts.clone();
page.on_console(move |msg| {
let cap = cap_clone.clone();
async move {
*cap.lock().unwrap() = Some(msg.timestamp());
Ok(())
}
})
.await?;
let _ = page.goto("about:blank", None).await;
let waiter = page.expect_console_message(Some(5000.0)).await?;
page.evaluate_expression("console.log('timestamp test')")
.await?;
waiter.wait().await.expect("console event did not fire");
let ts = captured_ts
.lock()
.unwrap()
.take()
.expect("on_console handler should have fired");
assert!(ts > 0.0, "timestamp should be positive, got: {ts}");
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_millis() as f64;
assert!(
(now_ms - ts).abs() < 60_000.0,
"timestamp should be near wall-clock time; got {ts} vs now {now_ms}"
);
browser.close().await?;
Ok(())
}
#[tokio::test]
async fn test_context_expect_console_message() -> Result<(), Box<dyn std::error::Error>> {
let (_pw, _browser, context) = crate::common::setup_context().await;
let page = context.new_page().await?;
let waiter = context.expect_console_message(None).await?;
page.evaluate_expression("console.log('expect-test')")
.await?;
let msg = waiter.wait().await?;
assert_eq!(msg.text(), "expect-test");
assert_eq!(msg.type_(), "log");
context.close().await?;
Ok(())
}