captchaforge 0.2.21

Automatic CAPTCHA detection and multi-strategy solving for chromiumoxide-driven headless browsers (Cloudflare Turnstile, reCAPTCHA v2/v3, hCaptcha, image grids, audio, sliders).
Documentation
//! Behavior-simulation integration tests.

use captchaforge::behavior::{
    click_realistic, idle_pause, micro_pause, mouse_move_bezier, scroll_realistic, type_realistic,
    ScrollDirection,
};

mod common;

#[tokio::test]
async fn mouse_move_bezier_does_not_panic() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"document.body.innerHTML = '<div id="target" style="position:absolute;left:400px;top:300px;width:10px;height:10px;"></div>'; 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    mouse_move_bezier(&page, 0.0, 0.0, 400.0, 300.0)
        .await
        .unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn click_realistic_does_not_panic() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"document.body.innerHTML = '<button id="btn">Click me</button>'; 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    click_realistic(&page, 50.0, 50.0).await.unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn type_realistic_does_not_panic() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"document.body.innerHTML = '<input id="in">'; 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    // Focus the input first.
    let _: String = page
        .evaluate(r#"document.getElementById('in').focus(); 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    type_realistic(&page, "hello").await.unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn scroll_realistic_does_not_panic() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"document.body.innerHTML = '<div style="height:2000px;"></div>'; 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    scroll_realistic(&page, ScrollDirection::Down, 500)
        .await
        .unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn idle_pause_completes() {
    let start = std::time::Instant::now();
    idle_pause().await;
    assert!(start.elapsed().as_millis() >= 400);
}

#[tokio::test]
async fn micro_pause_completes() {
    let start = std::time::Instant::now();
    micro_pause().await;
    assert!(start.elapsed().as_millis() >= 80);
}

#[tokio::test]
async fn scroll_realistic_up_direction() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"document.body.innerHTML = '<div style="height:2000px;"></div>'; window.scrollTo(0, 1000); 'done'"#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    scroll_realistic(&page, ScrollDirection::Up, 200)
        .await
        .unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn mouse_move_bezier_zero_distance() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    mouse_move_bezier(&page, 100.0, 100.0, 100.0, 100.0)
        .await
        .unwrap();
    browser.close().await.unwrap();
}

#[tokio::test]
async fn click_realistic_on_element_updates_dom() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate(r#"
            document.body.innerHTML = '<button id="btn" style="position:absolute;left:40px;top:40px;width:20px;height:20px;" onclick="this.innerText=\'clicked\'">click</button>';
            'done'
        "#)
        .await
        .unwrap()
        .into_value()
        .unwrap();
    click_realistic(&page, 50.0, 50.0).await.unwrap();
    let text: String = page
        .evaluate("document.getElementById('btn').innerText")
        .await
        .unwrap()
        .into_value()
        .unwrap();
    assert_eq!(text, "clicked");
    browser.close().await.unwrap();
}