rustenium 1.1.9

A modern, robust, high-performance WebDriver BiDi automation library for Rust
Documentation
use rustenium::browsers::{BidiBrowser, NavigateOptionsBuilder};
use rustenium::nodes::Node;
use rustenium::input::{Point, MouseClickOptions};
use rustenium_bidi_definitions::browsing_context::types::ReadinessState;
use rustenium_bidi_definitions::script::types::{RemoteValue, PrimitiveProtocolValue};
use rustenium_macros::css;

fn extract_string(value: &RemoteValue) -> Option<String> {
    match value {
        RemoteValue::PrimitiveProtocolValue(PrimitiveProtocolValue::StringValue(sv)) => {
            Some(sv.value.clone())
        }
        _ => None,
    }
}

fn extract_number(value: &RemoteValue) -> Option<f64> {
    match value {
        RemoteValue::PrimitiveProtocolValue(PrimitiveProtocolValue::NumberValue(nv)) => {
            nv.value.as_f64()
        }
        _ => None,
    }
}

fn nav_opts() -> rustenium::browsers::NavigateOptions {
    NavigateOptionsBuilder::default()
        .wait(ReadinessState::Complete)
        .build()
}

pub async fn test_create_and_close(browser: impl BidiBrowser) {
    browser.close().await.unwrap();
}

pub async fn test_navigate_to_url(mut browser: impl BidiBrowser) {
    let result = browser.navigate("https://example.com").await;
    assert!(result.is_ok());
    browser.close().await.unwrap();
}

pub async fn test_navigate_with_wait_complete(mut browser: impl BidiBrowser) {
    let result = browser.navigate_with_options("https://example.com", nav_opts()).await;
    assert!(result.is_ok());
    browser.close().await.unwrap();
}

pub async fn test_navigate_multiple_pages(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    browser.navigate_with_options("https://httpbin.org/html", nav_opts()).await.unwrap();
    browser.close().await.unwrap();
}

pub async fn test_find_nodes_body(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("body")).await.unwrap();
    assert!(!nodes.is_empty(), "Should find body element");
    browser.close().await.unwrap();
}

pub async fn test_find_nodes_heading(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    assert!(!nodes.is_empty(), "Should find h1 on example.com");
    browser.close().await.unwrap();
}

pub async fn test_find_nodes_nonexistent_returns_empty(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("#nonexistent-element-xyz")).await.unwrap();
    assert!(nodes.is_empty(), "Should return empty vec for nonexistent selector");
    browser.close().await.unwrap();
}

pub async fn test_find_node_returns_first(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let node = browser.find_node(css!("h1")).await.unwrap();
    assert!(node.is_some(), "Should find first h1");
    browser.close().await.unwrap();
}

pub async fn test_find_node_nonexistent_returns_none(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let node = browser.find_node(css!("#nonexistent-element-xyz")).await.unwrap();
    assert!(node.is_none(), "Should return None for nonexistent selector");
    browser.close().await.unwrap();
}

pub async fn test_wait_for_nodes_existing(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.wait_for_nodes(css!("h1")).await.unwrap();
    assert!(!nodes.is_empty(), "wait_for_nodes should find existing h1");
    browser.close().await.unwrap();
}

pub async fn test_wait_for_node_existing(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let node = browser.wait_for_node(css!("body")).await.unwrap();
    assert!(node.is_some(), "wait_for_node should find existing body");
    browser.close().await.unwrap();
}

pub async fn test_get_inner_text(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let text = nodes[0].get_inner_text().await;
    assert!(!text.is_empty(), "h1 inner text should not be empty");
    assert!(text.contains("Example Domain"), "h1 should contain 'Example Domain', got: {}", text);
    browser.close().await.unwrap();
}

pub async fn test_get_text_content(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let text = nodes[0].get_text_content().await;
    assert!(text.contains("Example Domain"), "text_content should contain 'Example Domain'");
    browser.close().await.unwrap();
}

pub async fn test_get_inner_html(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("body")).await.unwrap();
    let html = nodes[0].get_inner_html().await;
    assert!(html.contains("<h1>"), "body innerHTML should contain <h1>");
    browser.close().await.unwrap();
}

pub async fn test_get_attribute_href(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("a")).await.unwrap();
    assert!(!nodes.is_empty(), "Should find anchor elements");
    let href = nodes[0].get_attribute("href");
    assert!(href.is_some(), "Anchor should have href attribute");
    browser.close().await.unwrap();
}

pub async fn test_get_children_nodes(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("body")).await.unwrap();
    let children = nodes[0].get_children_nodes();
    assert!(!children.is_empty(), "body should have child nodes");
    browser.close().await.unwrap();
}

pub async fn test_node_context_id(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context_id = browser.get_active_context_id().unwrap();
    let nodes = browser.find_nodes(css!("body")).await.unwrap();
    assert_eq!(nodes[0].get_context_id(), &context_id);
    browser.close().await.unwrap();
}

pub async fn test_node_is_visible(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let visible = nodes[0].is_visible().await.unwrap();
    assert!(visible, "h1 should be visible");
    browser.close().await.unwrap();
}

pub async fn test_node_scroll_into_view(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("p")).await.unwrap();
    let result = nodes[0].scroll_into_view().await;
    assert!(result.is_ok(), "scroll_into_view should succeed");
    browser.close().await.unwrap();
}

pub async fn test_node_get_position(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let mut nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let pos = nodes[0].get_position().await;
    assert!(pos.is_some(), "h1 should have a position");
    let pos = pos.unwrap();
    assert!(pos.width > 0.0, "h1 width should be positive");
    assert!(pos.height > 0.0, "h1 height should be positive");
    browser.close().await.unwrap();
}

pub async fn test_node_mouse_move(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let mut nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let result = nodes[0].mouse_move().await;
    assert!(result.is_ok(), "mouse_move to h1 should succeed");
    browser.close().await.unwrap();
}

pub async fn test_node_mouse_click(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let mut nodes = browser.find_nodes(css!("a")).await.unwrap();
    assert!(!nodes.is_empty(), "Should find a link to click");
    let result = nodes[0].mouse_click().await;
    assert!(result.is_ok(), "mouse_click on link should succeed");
    browser.close().await.unwrap();
}

pub async fn test_mouse_move_to_point(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let mouse = browser.mouse();
    let result = mouse.move_to(Point { x: 100.0, y: 200.0 }, &context, Default::default()).await;
    assert!(result.is_ok(), "mouse move_to should succeed");
    browser.close().await.unwrap();
}

pub async fn test_mouse_click_at_point(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let mouse = browser.mouse();
    let result = mouse.click(Some(Point { x: 100.0, y: 100.0 }), &context, MouseClickOptions::default()).await;
    assert!(result.is_ok(), "mouse click should succeed");
    browser.close().await.unwrap();
}

pub async fn test_mouse_down_and_up(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let mouse = browser.mouse();
    mouse.down(&context, Default::default()).await.unwrap();
    mouse.up(&context, Default::default()).await.unwrap();
    browser.close().await.unwrap();
}

pub async fn test_keyboard_type_text(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let keyboard = browser.keyboard();
    let result = keyboard.type_text("hello", &context, None).await;
    assert!(result.is_ok(), "keyboard type_text should succeed");
    browser.close().await.unwrap();
}

pub async fn test_keyboard_press_key(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let keyboard = browser.keyboard();
    let result = keyboard.press("Enter", &context, None).await;
    assert!(result.is_ok(), "keyboard press Enter should succeed");
    browser.close().await.unwrap();
}

pub async fn test_keyboard_down_and_up(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let context = browser.get_active_context_id().unwrap();
    let keyboard = browser.keyboard();
    keyboard.down("Shift", &context).await.unwrap();
    keyboard.up("Shift", &context).await.unwrap();
    browser.close().await.unwrap();
}

pub async fn test_evaluate_script_simple(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let result = browser.evaluate_script("1 + 1".to_string(), false).await;
    assert!(result.is_ok(), "evaluate_script should succeed");
    browser.close().await.unwrap();
}

pub async fn test_evaluate_script_returns_string(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let result = browser.evaluate_script("document.title".to_string(), false).await.unwrap();
    let title = extract_string(&result.result).expect("Should return a string");
    assert!(title.contains("Example Domain"), "title should contain 'Example Domain', got: {}", title);
    browser.close().await.unwrap();
}

pub async fn test_evaluate_script_returns_number(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let result = browser.evaluate_script("document.querySelectorAll('p').length".to_string(), false).await.unwrap();
    let count = extract_number(&result.result).expect("Should return a number");
    assert!(count > 0.0, "Should find at least one paragraph");
    browser.close().await.unwrap();
}

pub async fn test_create_new_context(mut browser: impl BidiBrowser) {
    let new_context = browser.create_context(false).await;
    assert!(new_context.is_ok(), "create_context should succeed");
    browser.close().await.unwrap();
}

pub async fn test_get_active_context_id(browser: impl BidiBrowser) {
    let context = browser.get_active_context_id();
    assert!(context.is_ok(), "Should have an active context after creation");
    browser.close().await.unwrap();
}

pub async fn test_screenshot_returns_base64(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let data = browser.screenshot().await.unwrap();
    assert!(!data.is_empty(), "Screenshot data should not be empty");
    browser.close().await.unwrap();
}

pub async fn test_node_screenshot_returns_base64(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    let data = nodes[0].screenshot().await.unwrap();
    assert!(!data.is_empty(), "Node screenshot data should not be empty");
    browser.close().await.unwrap();
}

pub async fn test_emulate_timezone(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    browser.emulate_timezone(Some("America/New_York".to_string())).await.unwrap();
    let eval = browser.evaluate_script(
        "Intl.DateTimeFormat().resolvedOptions().timeZone".to_string(),
        false,
    ).await.unwrap();
    let tz = extract_string(&eval.result).expect("Should return timezone string");
    assert_eq!(tz, "America/New_York", "Timezone should be emulated");
    browser.close().await.unwrap();
}

pub async fn test_delete_node(mut browser: impl BidiBrowser) {
    browser.navigate_with_options("https://example.com", nav_opts()).await.unwrap();
    let nodes = browser.find_nodes(css!("h1")).await.unwrap();
    assert!(!nodes.is_empty());
    nodes[0].delete().await.unwrap();
    let nodes_after = browser.find_nodes(css!("h1")).await.unwrap();
    assert!(nodes_after.is_empty(), "h1 should be deleted");
    browser.close().await.unwrap();
}

pub async fn test_end_session_cleanly(mut browser: impl BidiBrowser) {
    let result = browser.end_session().await;
    assert!(result.is_ok(), "end_session should succeed");
}