captchaforge 0.2.36

[DO NOT USE — UNDER ACTIVE DEVELOPMENT, NOT PRODUCTION-READY] Captcha solver scaffolding for chromiumoxide-driven browsers. The architecture is in place (vendor solvers, retry-loop iframe walking, VLM provider abstraction, real-WAF bench harness) but the live-vendor success rate is still 0% — Cloudflare Turnstile / hCaptcha / reCAPTCHA detect us at a TLS / CDP fingerprint layer that no flag-based stealth has cleared. Watch the repo; do not depend on this for any real workload.
Documentation
//! Cross-module integration tests (detect → frame → behavior → solve).

use captchaforge::behavior::{click_realistic, mouse_move_bezier};
use captchaforge::detect::{detect, is_captcha};
use captchaforge::frame::find_element_centre_in_frames;
use captchaforge::solver::{BehavioralCaptchaSolver, CaptchaSolver, CaptchaSolverChain};

mod common;

#[tokio::test]
async fn detect_then_find_element_then_click() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let js = r#"
        document.body.innerHTML = '<div class="cf-turnstile" data-sitekey="test" style="position:absolute;left:100px;top:100px;width:50px;height:50px;"></div>';
        "true"
    "#;
    let _: String = page.evaluate(js).await.unwrap().into_value().unwrap();

    let info = detect(&page).await.unwrap();
    assert!(is_captcha(&info));

    let centre = find_element_centre_in_frames(&page, ".cf-turnstile")
        .await
        .unwrap();
    assert!(centre.is_some());

    let (x, y) = centre.unwrap();
    mouse_move_bezier(&page, 0.0, 0.0, x, y).await.unwrap();
    click_realistic(&page, x, y).await.unwrap();

    browser.close().await.unwrap();
}

#[tokio::test]
async fn detect_then_behavioral_solve_on_mock() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let js = r#"
        document.body.innerHTML = '<div class="cf-turnstile" data-sitekey="test"></div>';
        "true"
    "#;
    let _: String = page.evaluate(js).await.unwrap().into_value().unwrap();

    let info = detect(&page).await.unwrap();
    assert!(is_captcha(&info));

    let solver = BehavioralCaptchaSolver::new();
    let result = solver.solve(&page, &info).await.unwrap();
    // Mock page won't succeed but should return gracefully.
    assert!(!result.success || result.time_ms > 0);

    browser.close().await.unwrap();
}

#[tokio::test]
async fn default_chain_on_recaptcha_mock() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();

    let js = r#"
        document.body.innerHTML = '<div class="g-recaptcha" data-sitekey="test"></div>' +
            '<script src="https://www.google.com/recaptcha/api.js"></script>';
        "true"
    "#;
    let _: String = page.evaluate(js).await.unwrap().into_value().unwrap();

    let info = detect(&page).await.unwrap();
    assert!(is_captcha(&info));

    let chain = CaptchaSolverChain::default_chain();
    let result = chain.solve(&page, &info).await;
    assert!(!result.success || result.time_ms > 0);

    browser.close().await.unwrap();
}

#[tokio::test]
async fn plain_page_no_captcha_then_chain_unsolved() {
    let mut browser = common::launch_browser().await;
    let page = browser.new_page("about:blank").await.unwrap();
    let _: String = page
        .evaluate("document.body.innerHTML = '<h1>Safe</h1>'; 'done'")
        .await
        .unwrap()
        .into_value()
        .unwrap();

    let info = detect(&page).await.unwrap();
    assert!(!is_captcha(&info));

    let chain = CaptchaSolverChain::default_chain();
    let result = chain.solve(&page, &info).await;
    assert!(!result.success);

    browser.close().await.unwrap();
}