browser_oxide 0.1.3

Stealth headless browser engine in Rust: real HTML/CSS/DOM/JS, V8 via deno_core, own BoringSSL TLS/JA4 fingerprint, no Chromium, no CDP — for anti-bot web scraping, archival, and AI agents
Documentation
//! Chrome 147 parity tests — direct comparison of browser_oxide outputs
//! against captured real Chrome 147 ground truth in
//! `tests/fixtures/chrome147/captured_macos_arm64.json`.
//!
//! The fixture was captured on 2026-04-28 from Google Chrome 147.0.7727.117
//! running headless on macOS arm64 via a CDP automation driver. This is the same
//! Chrome that a reference browser capture launches, so passing these tests means
//! browser_oxide produces engine-coherent output by the same metrics
//! anti-bot detectors apply.

use browser_oxide::Page;

async fn evaluate(js: &str) -> String {
    let mut page = Page::from_html(
        "<!DOCTYPE html><html><body></body></html>",
        None::<browser_oxide::stealth::StealthProfile>,
    )
    .await
    .unwrap();
    page.evaluate(js).unwrap_or_else(|e| format!("ERROR: {e}"))
}

/// Same as `evaluate` but the page is loaded with an https:// URL so
/// SecureContext-only Web Platform APIs (Notification.permission,
/// userAgentData, deviceMemory, permissions, ...) behave per Chrome.
async fn evaluate_secure(js: &str) -> String {
    let mut page = Page::from_html_with_url(
        "<!DOCTYPE html><html><body></body></html>",
        "https://example.com/",
        None::<browser_oxide::stealth::StealthProfile>,
    )
    .await
    .unwrap();
    page.evaluate(js).unwrap_or_else(|e| format!("ERROR: {e}"))
}

// ================================================================
// V8 / engine identifiers — exact match to Chrome 147
// ================================================================
#[tokio::test]
async fn parity_eval_to_string_length() {
    let r = evaluate("eval.toString().length").await;
    assert_eq!(r, "33", "Chrome 147: eval.toString().length === 33");
}

#[tokio::test]
async fn parity_eval_to_string_exact() {
    let r = evaluate("eval.toString()").await;
    assert_eq!(
        r, "function eval() { [native code] }",
        "Chrome 147: eval.toString() === 'function eval() {{ [native code] }}'"
    );
}

#[tokio::test]
#[allow(non_snake_case, reason = "mirrors JS API name under test")]
async fn parity_function_toString_self_native() {
    let r = evaluate("Function.prototype.toString.call(Function.prototype.toString)").await;
    assert_eq!(
        r, "function toString() { [native code] }",
        "Chrome 147: Function.prototype.toString.toString() === 'function toString() {{ [native code] }}'"
    );
}

#[tokio::test]
async fn parity_math_sin_to_string() {
    let r = evaluate("Function.prototype.toString.call(Math.sin)").await;
    assert_eq!(r, "function sin() { [native code] }");
}

// ================================================================
// Math constants (CreepJS lookup table) — Chrome 147 V8 14.x
// ================================================================
#[tokio::test]
async fn parity_math_cos_13e() {
    let r = evaluate("Math.cos(13 * Math.E)").await;
    // Real Chrome 147: -0.7108118501064332
    assert_eq!(
        r, "-0.7108118501064332",
        "Math.cos(13*Math.E) must match Chrome 147 V8 byte-for-byte"
    );
}

#[tokio::test]
async fn parity_math_cos_57e() {
    let r = evaluate("Math.cos(57 * Math.E)").await;
    assert_eq!(r, "-0.536911695749024");
}

#[tokio::test]
async fn parity_math_acos_0_123() {
    let r = evaluate("Math.acos(0.123)").await;
    assert_eq!(r, "1.4474840516030247");
}

#[tokio::test]
async fn parity_math_acosh_1e308() {
    let r = evaluate("Math.acosh(1e308)").await;
    assert_eq!(r, "709.889355822726");
}

#[tokio::test]
async fn parity_math_atan_2() {
    let r = evaluate("Math.atan(2)").await;
    assert_eq!(r, "1.1071487177940904");
}

#[tokio::test]
async fn parity_math_tan_neg1e300() {
    let r = evaluate("Math.tan(-1e300)").await;
    assert_eq!(r, "-1.4214488238747245");
}

// ================================================================
// Sub-pixel layout — Blink LayoutUnit emits 1/64-px floats
// ================================================================
#[tokio::test]
async fn parity_rect_width_1_3px() {
    // Real Chrome 147: width:1.3px div → 1.296875 (= 83/64)
    let r = evaluate(
        "
        const d = document.createElement('div');
        d.style.cssText = 'width:1.3px;height:0.5px;position:absolute;';
        document.body.appendChild(d);
        const w = d.getBoundingClientRect().width;
        // Either exact match (1.296875) or a multiple of 1/64 close enough
        Number.isInteger(Math.round(w * 64))
        ",
    )
    .await;
    assert_eq!(
        r, "true",
        "rect width must be a multiple of 1/64 px (LayoutUnit)"
    );
}

#[tokio::test]
async fn parity_rect_height_0_5px() {
    let r = evaluate(
        "
        const d = document.createElement('div');
        d.style.cssText = 'width:1.3px;height:0.5px;position:absolute;';
        document.body.appendChild(d);
        const h = d.getBoundingClientRect().height;
        Number.isInteger(Math.round(h * 64))
        ",
    )
    .await;
    assert_eq!(r, "true");
}

// ================================================================
// Iframe realm purity — exact bool match to Chrome 147
// ================================================================
#[tokio::test]
#[ignore = "not yet implemented: per-realm constructor identity for iframe contexts"]
async fn parity_iframe_navigator_distinct() {
    let r = evaluate(
        "(()=>{ const f = document.createElement('iframe'); document.body.appendChild(f); return f.contentWindow.Navigator !== Navigator; })()",
    )
    .await;
    assert_eq!(r, "true", "Chrome 147 returns true; engine must match");
}

#[tokio::test]
async fn parity_iframe_array_distinct() {
    let r = evaluate(
        "(()=>{ const f = document.createElement('iframe'); document.body.appendChild(f); return f.contentWindow.Array !== Array; })()",
    )
    .await;
    assert_eq!(r, "true");
}

/// Real Chrome 147: `[] instanceof iframe.Array === false`.
#[tokio::test]
async fn parity_iframe_array_instanceof_is_false() {
    let r = evaluate(
        "(()=>{ const f = document.createElement('iframe'); document.body.appendChild(f); return ([] instanceof f.contentWindow.Array); })()",
    )
    .await;
    assert_eq!(
        r, "false",
        "Chrome 147 returns false; cross-realm instanceof must be false"
    );
}

#[tokio::test]
#[allow(non_snake_case, reason = "mirrors JS API name under test")]
async fn parity_iframe_function_toString_native() {
    let r = evaluate(
        "(()=>{ const f = document.createElement('iframe'); document.body.appendChild(f); return f.contentWindow.Function.prototype.toString.call(window.fetch).includes('[native code]'); })()",
    )
    .await;
    assert_eq!(r, "true");
}

// ================================================================
// Navigator surface — exact match
// ================================================================
#[tokio::test]
async fn parity_navigator_vendor() {
    let r = evaluate("navigator.vendor").await;
    assert_eq!(r, "Google Inc.");
}

#[tokio::test]
async fn parity_navigator_product_sub() {
    let r = evaluate("navigator.productSub").await;
    assert_eq!(r, "20030107");
}

#[tokio::test]
async fn parity_navigator_product() {
    let r = evaluate("navigator.product").await;
    assert_eq!(r, "Gecko");
}

#[tokio::test]
async fn parity_navigator_app_code_name() {
    let r = evaluate("navigator.appCodeName").await;
    assert_eq!(r, "Mozilla");
}

#[tokio::test]
async fn parity_navigator_webdriver_false() {
    // Real Chrome (non-automation): false. browser_oxide must always be false.
    let r = evaluate("navigator.webdriver").await;
    assert_eq!(r, "false");
}

// ================================================================
// chrome.* legacy stubs (Chrome desktop ships these)
// ================================================================
#[tokio::test]
async fn parity_chrome_app_exists() {
    let r = evaluate("typeof chrome !== 'undefined' && typeof chrome.app === 'object'").await;
    assert_eq!(r, "true");
}

/// Real non-automation Chrome 147 on a regular page does NOT expose
/// `chrome.runtime` — only extension contexts do. The captured fixture
/// shows it `true` because a CDP automation driver launches in automation mode,
/// which is exactly the signal anti-bot detectors flag. browser_oxide
/// correctly omits `chrome.runtime` to look like a clean user session.
#[tokio::test]
async fn parity_chrome_runtime_absent_like_user_session() {
    let r = evaluate("typeof chrome !== 'undefined' && typeof chrome.runtime").await;
    assert_eq!(
        r, "undefined",
        "chrome.runtime must be absent (presence is automation-mode tell)"
    );
}

// chrome.app sub-shape (Chromium-source-documented surface). A bot
// detector that enumerates `Object.getOwnPropertyNames(chrome.app)` or
// calls each method gets the same return values as real Chrome 147.

#[tokio::test]
async fn parity_chrome_app_own_property_names() {
    let r = evaluate("Object.getOwnPropertyNames(chrome.app).sort().join(',')").await;
    assert_eq!(
        r,
        "InstallState,RunningState,getDetails,getIsInstalled,installState,isInstalled,runningState"
    );
}

#[tokio::test]
async fn parity_chrome_app_get_details_returns_null() {
    let r = evaluate("chrome.app.getDetails()").await;
    assert_eq!(r, "null", "getDetails() off-CWS must return null");
}

#[tokio::test]
async fn parity_chrome_app_get_is_installed_returns_false() {
    let r = evaluate("chrome.app.getIsInstalled()").await;
    assert_eq!(r, "false");
}

#[tokio::test]
async fn parity_chrome_app_running_state_returns_cannot_run() {
    let r = evaluate("chrome.app.runningState()").await;
    assert_eq!(r, "cannot_run");
}

#[tokio::test]
async fn parity_chrome_app_install_state_async_callback_fires() {
    // installState is async (callback). Kick it off, drain the event loop
    // so the setTimeout(0)-deferred callback fires, then read the global.
    // Real Chrome 147 calls back with 'not_installed' when invoked off
    // the Web Store.
    let mut page = Page::from_html(
        "<!DOCTYPE html><html><body></body></html>",
        None::<browser_oxide::stealth::StealthProfile>,
    )
    .await
    .unwrap();
    page.evaluate(
        r#"window.__installStateResult = null;
        chrome.app.installState(s => { window.__installStateResult = s; });"#,
    )
    .unwrap();
    page.evaluate_async("void 0", std::time::Duration::from_millis(200))
        .await
        .ok();
    let r = page.evaluate("window.__installStateResult").unwrap();
    assert_eq!(
        r, "not_installed",
        "installState callback must fire with 'not_installed'"
    );
}

#[tokio::test]
async fn parity_chrome_app_install_state_dict() {
    let r = evaluate(
        "JSON.stringify([chrome.app.InstallState.DISABLED, chrome.app.InstallState.INSTALLED, chrome.app.InstallState.NOT_INSTALLED])",
    )
    .await;
    assert_eq!(r, "[\"disabled\",\"installed\",\"not_installed\"]");
}

#[tokio::test]
async fn parity_chrome_app_running_state_dict() {
    let r = evaluate(
        "JSON.stringify([chrome.app.RunningState.CANNOT_RUN, chrome.app.RunningState.READY_TO_RUN, chrome.app.RunningState.RUNNING])",
    )
    .await;
    assert_eq!(r, "[\"cannot_run\",\"ready_to_run\",\"running\"]");
}

// ================================================================
// Permissions matrix
// ================================================================
#[tokio::test]
async fn parity_notification_permission() {
    // Real Chrome 147 default profile on a secure page: 'default'.
    // (On http:// real Chrome reports 'denied' — see anti_bot.rs.)
    let r = evaluate_secure("Notification.permission").await;
    assert_eq!(r, "default");
}

// ================================================================
// V8 surface that exists in Chrome 147
// ================================================================
#[tokio::test]
async fn parity_promise_with_resolvers() {
    let r = evaluate("typeof Promise.withResolvers").await;
    assert_eq!(r, "function");
}

#[tokio::test]
async fn parity_structured_clone() {
    let r = evaluate("typeof structuredClone").await;
    assert_eq!(r, "function");
}

// ================================================================
// Object.getOwnPropertyNames(window) approximate count check.
// Real Chrome 147 headless: 980. browser_oxide should be in the
// same order of magnitude (≥ 500 — the test asserts the surface
// is broad, not byte-exact since OS/build varies).
// ================================================================
#[tokio::test]
async fn parity_globals_count_in_range() {
    let r = evaluate("Object.getOwnPropertyNames(globalThis).length").await;
    let n: usize = r.parse().unwrap_or(0);
    assert!(
        n >= 200,
        "globals count must be ≥ 200 (real Chrome 147 headless: 980); got {n}"
    );
}