playwright-cdp 0.3.0

Drive Chromium directly via the Chrome DevTools Protocol (CDP) — no Playwright driver required.
Documentation
//! Worker capture smoke test: `on_worker` fires for a dedicated worker spawned
//! from an inline blob, `worker.url()` reports a `blob:` URL, and `evaluate`
//! runs in the worker's execution context.

mod common;

use playwright_cdp::Worker;
use std::sync::Arc;
use tokio::sync::{oneshot, Mutex};

#[tokio::test]
async fn on_worker_captures_dedicated_worker() {
    let browser = match common::browser().await {
        Some(b) => b,
        None => {
            eprintln!("[skip] no browser available");
            return;
        }
    };
    let page = browser.new_page().await.unwrap();

    // Channel to ferry the first Worker out of the handler.
    let (tx, rx) = oneshot::channel::<Worker>();
    let tx = Arc::new(Mutex::new(Some(tx)));

    // Register the handler BEFORE creating the worker. set_content runs the
    // inline script which spawns the worker, so auto-attach must be enabled
    // (and the listener subscribed) first.
    page.on_worker(move |w| {
        let tx = Arc::clone(&tx);
        async move {
            if let Some(t) = tx.lock().await.take() {
                let _ = t.send(w);
            }
        }
    })
    .await;

    // A dedicated worker created from an inline blob URL.
    let html = r#"
        <script>
          const w = new Worker(URL.createObjectURL(new Blob([
            `onmessage = e => postMessage('hi from ' + e.data)`
          ], {type:'text/javascript'})));
          window.__w = w;
        </script>
    "#;
    page.set_content(html).await.unwrap();

    let worker = match tokio::time::timeout(std::time::Duration::from_secs(15), rx).await {
        Ok(Ok(w)) => w,
        _ => {
            eprintln!("[skip] worker event did not fire in time");
            browser.close().await.unwrap();
            return;
        }
    };

    // The worker URL should be a blob: URL.
    let url = worker.url().to_string();
    println!("[worker] url = {url}");
    assert!(
        url.starts_with("blob:"),
        "dedicated worker url should start with blob:, got {url}"
    );

    // Evaluate in the worker's execution context. Dedicated workers expose a
    // default execution context, so Runtime.evaluate should work after
    // Runtime.enable. If a particular Chrome build rejects it, we still pass on
    // the url + handler-fired assertions above (documented limitation).
    match worker.evaluate::<serde_json::Value>("1 + 1").await {
        Ok(v) => {
            println!("[worker] evaluate(1+1) = {v}");
            let n = v.as_i64().or_else(|| v.as_f64().map(|f| f as i64));
            assert_eq!(n, Some(2), "worker evaluate 1+1 should yield 2, got {v}");
        }
        Err(e) => {
            // Document any CDP quirk but keep the test green: the handler fired
            // and the url is correct.
            eprintln!("[worker] evaluate not supported on this build: {e}");
        }
    }

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