#![allow(dead_code)]
use std::{
net::SocketAddr,
sync::{Arc, OnceLock},
thread::JoinHandle,
};
use rstest::fixture;
use thirtyfour::prelude::*;
use thirtyfour::support::block_on;
use tokio::sync::{Semaphore, SemaphorePermit};
static SERVER: OnceLock<Arc<JoinHandle<()>>> = OnceLock::new();
static LOGINIT: OnceLock<()> = OnceLock::new();
const ASSETS_DIR: &str = "tests/test_html";
const PORT: u16 = 8081;
pub fn make_capabilities(s: &str) -> Capabilities {
match s {
"firefox" => {
let mut caps = DesiredCapabilities::firefox();
caps.set_headless().unwrap();
caps.into()
}
"chrome" => {
let mut caps = DesiredCapabilities::chrome();
caps.set_headless().unwrap();
caps.set_no_sandbox().unwrap();
caps.set_disable_gpu().unwrap();
caps.set_disable_dev_shm_usage().unwrap();
caps.add_arg("--no-sandbox").unwrap();
caps.into()
}
browser => unimplemented!("unsupported browser backend {}", browser),
}
}
pub fn webdriver_url(s: &str) -> String {
match s {
"firefox" => "http://localhost:4444".to_string(),
"chrome" => "http://localhost:9515".to_string(),
browser => unimplemented!("unsupported browser backend {}", browser),
}
}
pub fn start_server() -> Arc<JoinHandle<()>> {
SERVER
.get_or_init(|| {
let handle = std::thread::spawn(move || {
let rt =
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
rt.block_on(async {
tracing::debug!("starting web server on http://localhost:{PORT}");
let addr = SocketAddr::from(([127, 0, 0, 1], PORT));
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
let app = axum::Router::new()
.fallback_service(tower_http::services::ServeDir::new(ASSETS_DIR));
axum::serve(listener, app).await.unwrap();
});
});
Arc::new(handle)
})
.clone()
}
pub fn init_logging() {
LOGINIT.get_or_init(|| {
use tracing_subscriber::{EnvFilter, fmt, prelude::*};
tracing_subscriber::registry()
.with(fmt::layer())
.with(EnvFilter::from_default_env())
.init();
});
}
pub fn get_limiter() -> &'static Semaphore {
static LIMITER: Semaphore = Semaphore::const_new(1);
&LIMITER
}
pub async fn lock_firefox(browser: &str) -> Option<SemaphorePermit<'static>> {
if browser == "firefox" {
Some(get_limiter().acquire().await.unwrap())
} else {
None
}
}
pub async fn launch_browser(browser: &str) -> WebDriver {
tracing::debug!("launching browser {browser}");
let caps = make_capabilities(browser);
let webdriver_url = webdriver_url(browser);
WebDriver::new(webdriver_url, caps).await.expect("Failed to create WebDriver")
}
pub struct TestHarness {
browser: String,
server: Arc<JoinHandle<()>>,
driver: Option<WebDriver>,
guard: Option<SemaphorePermit<'static>>,
}
impl TestHarness {
pub async fn new(browser: &str) -> Self {
let server = start_server();
let guard = lock_firefox(browser).await;
let driver = Some(launch_browser(browser).await);
Self {
browser: browser.to_string(),
server,
driver,
guard,
}
}
pub fn browser(&self) -> &str {
&self.browser
}
pub fn driver(&self) -> &WebDriver {
self.driver.as_ref().expect("the driver to still be active")
}
pub fn disable_auto_close(mut self) -> Self {
if let Some(driver) = self.driver.take() {
let _ = driver.leak();
}
self
}
}
#[fixture]
pub fn test_harness() -> TestHarness {
let browser = std::env::var("THIRTYFOUR_BROWSER").unwrap_or_else(|_| "chrome".to_string());
init_logging();
block_on(TestHarness::new(&browser))
}
pub fn sample_page_url() -> String {
format!("http://localhost:{PORT}/sample_page.html")
}
pub fn other_page_url() -> String {
format!("http://localhost:{PORT}/other_page.html")
}
pub fn drag_to_url() -> String {
format!("http://localhost:{PORT}/drag_to.html")
}