use crate::common;
use crate::test_server::TestServer;
use std::sync::{Arc, Mutex};
use tokio::sync::Notify;
#[tokio::test]
async fn test_websocket_url() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
let captured_url: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
let captured_url_clone = captured_url.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
page.on_websocket(move |ws| {
let url_store = captured_url_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
*url_store.lock().unwrap() = Some(ws.url().to_string());
n.notify_one();
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("websocket event did not fire");
let url = captured_url.lock().unwrap().clone();
assert!(url.is_some(), "WebSocket URL should have been captured");
let url = url.unwrap();
assert!(url.contains("/ws"), "URL should contain /ws, got: {url}");
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
async fn test_websocket_is_closed() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
let captured_ws: Arc<Mutex<Option<playwright_rs::protocol::WebSocket>>> =
Arc::new(Mutex::new(None));
let captured_ws_clone = captured_ws.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
page.on_websocket(move |ws| {
let ws_store = captured_ws_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
*ws_store.lock().unwrap() = Some(ws);
n.notify_one();
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("websocket event did not fire");
let ws = captured_ws.lock().unwrap().clone();
assert!(ws.is_some(), "WebSocket should have been captured");
let ws = ws.unwrap();
assert!(!ws.is_closed(), "WebSocket should not be closed initially");
let close_waiter = ws.expect_close(Some(5000.0)).await.unwrap();
page.close().await.unwrap();
close_waiter
.wait()
.await
.expect("expect_close waiter should resolve after page close");
assert!(
ws.is_closed(),
"WebSocket should be closed after waiter resolves"
);
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
#[ignore] async fn test_websocket_frame_received() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
let (waiter_tx, waiter_rx) =
tokio::sync::oneshot::channel::<playwright_rs::EventWaiter<String>>();
let waiter_tx = Arc::new(Mutex::new(Some(waiter_tx)));
page.on_websocket(move |ws| {
let tx_store = waiter_tx.clone();
Box::pin(async move {
let waiter = ws.expect_frame_received(Some(5000.0)).await?;
if let Some(tx) = tx_store.lock().unwrap().take() {
let _ = tx.send(waiter);
}
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
let waiter = tokio::time::timeout(std::time::Duration::from_secs(5), waiter_rx)
.await
.expect("on_websocket did not fire in time")
.expect("waiter_tx was dropped without sending");
let frame = waiter
.wait()
.await
.expect("should have received at least one frame (echo from server)");
assert!(!frame.is_empty(), "Received frame should not be empty");
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
async fn test_websocket_expect_close() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
let captured_ws: Arc<Mutex<Option<playwright_rs::protocol::WebSocket>>> =
Arc::new(Mutex::new(None));
let captured_ws_clone = captured_ws.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
page.on_websocket(move |ws| {
let ws_store = captured_ws_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
*ws_store.lock().unwrap() = Some(ws);
n.notify_one();
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("websocket event did not fire");
let ws = captured_ws.lock().unwrap().clone();
assert!(ws.is_some(), "WebSocket should have been captured");
let ws = ws.unwrap();
let waiter = ws.expect_close(Some(5000.0)).await.unwrap();
page.close().await.unwrap();
waiter
.wait()
.await
.expect("expect_close waiter should resolve after page close");
assert!(
ws.is_closed(),
"WebSocket should be closed after waiter resolves"
);
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
#[ignore] async fn test_websocket_expect_frame_received_api() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
let captured_ws: Arc<Mutex<Option<playwright_rs::protocol::WebSocket>>> =
Arc::new(Mutex::new(None));
let captured_ws_clone = captured_ws.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
page.on_websocket(move |ws| {
let ws_store = captured_ws_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
*ws_store.lock().unwrap() = Some(ws);
n.notify_one();
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("websocket event did not fire");
let ws = captured_ws.lock().unwrap().clone();
assert!(ws.is_some(), "WebSocket should have been captured");
let ws = ws.unwrap();
let _waiter = ws
.expect_frame_received(Some(2000.0))
.await
.expect("expect_frame_received should return Ok");
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
async fn test_page_route_web_socket() {
let (_pw, browser, page) = common::setup().await;
let server = TestServer::start().await;
use std::sync::atomic::{AtomicBool, Ordering};
let handler_called = Arc::new(AtomicBool::new(false));
let handler_called_clone = handler_called.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
let ws_url = server.url().replace("http://", "ws://") + "/ws";
page.route_web_socket(&ws_url, move |route| {
let called = handler_called_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
called.store(true, Ordering::Release);
n.notify_one();
route.connect_to_server().await?;
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("WebSocket route handler did not fire");
assert!(
handler_called.load(Ordering::Acquire),
"WebSocket route handler should have been called"
);
browser.close().await.unwrap();
server.shutdown();
}
#[tokio::test]
async fn test_context_route_web_socket() {
let (_pw, browser, context) = common::setup_context().await;
let server = TestServer::start().await;
let page = context.new_page().await.unwrap();
use std::sync::atomic::{AtomicBool, Ordering};
let handler_called = Arc::new(AtomicBool::new(false));
let handler_called_clone = handler_called.clone();
let notify = Arc::new(Notify::new());
let notify_clone = notify.clone();
let ws_url = server.url().replace("http://", "ws://") + "/ws";
context
.route_web_socket(&ws_url, move |route| {
let called = handler_called_clone.clone();
let n = notify_clone.clone();
Box::pin(async move {
called.store(true, Ordering::Release);
n.notify_one();
route.connect_to_server().await?;
Ok(())
})
})
.await
.unwrap();
page.goto(&format!("{}/websocket.html", server.url()), None)
.await
.unwrap();
tokio::time::timeout(std::time::Duration::from_secs(5), notify.notified())
.await
.expect("Context WebSocket route handler did not fire");
assert!(
handler_called.load(Ordering::Acquire),
"Context WebSocket route handler should have been called"
);
context.close().await.unwrap();
browser.close().await.unwrap();
server.shutdown();
}