playwright-rs 0.15.1

Rust bindings for Microsoft Playwright
Documentation
// playwright.request — public APIRequest for headless API testing

use playwright_rs::protocol::Playwright;
use playwright_rs::{APIRequestContextOptions, APIResponse};

use crate::test_server::TestServer;

#[tokio::test]
async fn test_api_request_get() {
    crate::common::init_tracing();
    let server = TestServer::start().await;

    let playwright = Playwright::launch()
        .await
        .expect("setup: failed to launch Playwright");

    let ctx = playwright
        .request()
        .new_context(None)
        .await
        .expect("Failed to create APIRequestContext");

    let url = format!("{}/api/data.json", server.url());
    let response: APIResponse = ctx.get(&url, None).await.expect("GET should succeed");

    assert_eq!(response.status(), 200);
    assert!(response.ok());

    #[derive(serde::Deserialize)]
    struct Data {
        status: String,
        message: String,
    }

    let data: Data = response.json().await.expect("JSON parse should succeed");
    assert_eq!(data.status, "ok");
    assert_eq!(data.message, "hello from test server");

    ctx.dispose().await.expect("dispose should succeed");
    playwright
        .shutdown()
        .await
        .expect("shutdown should succeed");
    server.shutdown();
}

#[tokio::test]
async fn test_api_request_post() {
    crate::common::init_tracing();
    let server = TestServer::start().await;

    let playwright = Playwright::launch()
        .await
        .expect("setup: failed to launch Playwright");

    let ctx = playwright
        .request()
        .new_context(None)
        .await
        .expect("Failed to create APIRequestContext");

    let url = format!("{}/api/echo", server.url());

    use playwright_rs::FetchOptions;
    let opts = FetchOptions::builder()
        .method("POST".to_string())
        .post_data("hello post".to_string())
        .build();

    let response = ctx
        .post(&url, Some(opts))
        .await
        .expect("POST should succeed");

    assert_eq!(response.status(), 200);
    let body = response.text().await.expect("text() should succeed");
    assert!(body.contains("hello post"));

    ctx.dispose().await.expect("dispose should succeed");
    playwright
        .shutdown()
        .await
        .expect("shutdown should succeed");
    server.shutdown();
}

#[tokio::test]
async fn test_api_request_with_base_url() {
    crate::common::init_tracing();
    let server = TestServer::start().await;

    let playwright = Playwright::launch()
        .await
        .expect("setup: failed to launch Playwright");

    let opts = APIRequestContextOptions::default().base_url(server.url());

    let ctx = playwright
        .request()
        .new_context(Some(opts))
        .await
        .expect("Failed to create APIRequestContext with base_url");

    let response = ctx
        .get("/api/data.json", None)
        .await
        .expect("GET with relative URL should succeed");

    assert_eq!(response.status(), 200);

    ctx.dispose().await.expect("dispose should succeed");
    playwright
        .shutdown()
        .await
        .expect("shutdown should succeed");
    server.shutdown();
}

#[tokio::test]
async fn test_api_request_dispose() {
    crate::common::init_tracing();

    let playwright = Playwright::launch()
        .await
        .expect("setup: failed to launch Playwright");

    let ctx = playwright
        .request()
        .new_context(None)
        .await
        .expect("Failed to create APIRequestContext");

    ctx.dispose().await.expect("dispose() should succeed");
    playwright
        .shutdown()
        .await
        .expect("shutdown should succeed");
}

#[tokio::test]
async fn test_api_response_server_addr_and_security_details() {
    crate::common::init_tracing();
    let server = TestServer::start().await;

    let playwright = Playwright::launch()
        .await
        .expect("setup: failed to launch Playwright");
    let ctx = playwright
        .request()
        .new_context(None)
        .await
        .expect("Failed to create APIRequestContext");

    let url = format!("{}/api/data.json", server.url());
    let response: APIResponse = ctx.get(&url, None).await.expect("GET should succeed");

    // Plain HTTP carries no TLS details, and the server omits a remote address
    // for this fetch. The accessors must resolve cleanly (the new initializer
    // fields must not break fetch deserialization).
    assert!(
        response.security_details().is_none(),
        "plain HTTP should have no security details"
    );
    if let Some(addr) = response.server_addr() {
        assert!(addr.port > 0, "if present, server port should be set");
    }

    // HTTPS should populate both accessors (the populated case, matching the
    // `page.goto("https://example.com", ...)` precedent used elsewhere in
    // this suite for real-network TLS coverage).
    let https_response: APIResponse = ctx
        .get("https://example.com", None)
        .await
        .expect("HTTPS GET should succeed");
    let security_details = https_response
        .security_details()
        .expect("HTTPS response should carry security details");
    assert!(
        security_details.protocol.as_deref().unwrap_or_default() != "",
        "security details should report a TLS protocol version"
    );
    let addr = https_response
        .server_addr()
        .expect("HTTPS response should carry a server address");
    assert_eq!(addr.port, 443, "HTTPS server address should be port 443");

    ctx.dispose().await.expect("dispose should succeed");
    playwright
        .shutdown()
        .await
        .expect("shutdown should succeed");
    server.shutdown();
}