playwright-rust

repository·master·Indexed 19 days ago

https://github.com/octaltree/playwright-rust

A Rust port of the Playwright library for browser automation of Chromium, Firefox, and WebKit. Built on top of the Node.js Playwright library, it provides a Rust-idiomatic interface using the builder pattern for optional arguments. It supports multiple async runtimes, including tokio (default), actix-rt, and async-std. Key features include isolated BrowserContexts, accessibility tree inspection, and support for persistent browser contexts.

Tokens
15.6K
Snippets
50
Records
64
Agent score
66%

What's inside playwright-rust

  1. API Pattern: Builder Pattern for optional arguments

    master
    Because Rust does not support default arguments, playwright-rust uses the builder pattern for any functions that require two or more optional arguments. Instead of passing arguments directly to a function, you use a _builder() method to configure the options before calling the final execution method.
  2. Supported async runtimes

    master

    Playwright supports several async runtimes. By default, the tokio feature is enabled. If you wish to use a different runtime, you must disable the default tokio feature and select your preferred runtime from the following list:

    • tokio (default)
    • actix-rt
    • async-std
  3. How the Playwright Driver works

    master

    Playwright operates on a server-client architecture. The Rust client depends on a driver, which is a zip containing the core JavaScript library and Node.js.

    When you build your application, this driver is bundled into your Rust binary. Note that there is an overhead of unzipping the driver during the first run of the application.

  4. Basic usage of playwright

    master

    To use Playwright, initialize the driver, prepare the browsers, and then launch a specific browser type (Chromium, Firefox, or WebKit). The library uses a builder pattern for functions with multiple optional arguments to compensate for Rust's lack of default arguments.

    use playwright::Playwright;
    
    #[tokio::main]
    async fn main() -> Result<(), playwright::Error> {
        let playwright = Playwright::initialize().await?;
        playwright.prepare()?; // Install browsers
        let chromium = playwright.chromium();
        let browser = chromium.launcher().headless(true).launch().await?;
        let context = browser.context_builder().build().await?;
        let page = context.new_page().await?;
        page.goto_builder("https://example.com/").goto().await?;
    
        // Exec in browser and Deserialize with serde
        let s: String = page.eval("() => location.href").await?;
        assert_eq!(s, "https://example.com/");
        page.click_builder("a").click().await?;
        Ok(())
    }
  5. Interact with a Page using the Page API

    master

    The Page struct provides methods to control a single tab or a Chromium extension background page. It is the primary interface for browser automation tasks like navigation, element interaction, and script execution.

    Common tasks include:

    • Navigation: Using goto_builder() to navigate to a URL.
    • Interaction: Using builders like click_builder(), fill_builder(), or type_builder() to interact with elements.
    • Evaluation: Running JavaScript via eval() or evaluate() to interact with the page context.
    • Screenshots/PDFs: Capturing the page state using screenshot_builder() or pdf_builder().
    // Conceptual example of Page usage
    let browser = await chromium.launch();
    let context = await browser.new_context();
    let page = await context.new_page();
    
    await page.goto_builder("https://example.com").wait_until(DocumentLoadState::Load).reload().await?;
    await page.click_builder("#submit-button").click().await?;
    let title = await page.title().await?;
  6. How the accessibility tree snapshot works

    master

    The accessibility tree is a representation of the page used by assistive technologies. Because different platforms and screen readers interpret this tree differently, Playwright provides an abstraction to access this tree directly from the browser engine (Chromium, Firefox, or WebKit).

    To make the tree easier to process, Playwright's snapshot() method defaults to interesting_only: true, which discards nodes that are typically ignored by most screen readers. You can traverse the returned SnapshotResponse (which represents the root node) by recursively checking its children property.

    // Conceptual logic for finding a focused node in a snapshot
    fn find_focused_node(node: &SnapshotResponse) -> Option<&SnapshotResponse> {
        if node.focused {
            return Some(node);
        }
        for child in node.children.as_ref()? {
            if let Some(found) = find_focused_node(child) {
                return Some(found);
            }
        }
        None
    }
  7. Manage isolated sessions with BrowserContext

    master

    A BrowserContext provides an isolated browser session. Using multiple contexts allows you to run multiple independent sessions (e.g., different users or 'incognito' modes) within a single browser instance.

    Key behaviors:

    • Incognito Mode: Contexts created via browser.newContext() do not write browsing data to disk.
    • Isolation: Cookies, permissions, and local storage are scoped to the context. If a page opens a popup, that popup belongs to the parent page's context.
    • Lifecycle: You must call .close() explicitly to close a context. Note that the default browser context cannot be closed.
    // Example of creating a new page within a context
    let context = browser.new_context().await?;
    let page = context.new_page().await?;
  8. Handle browser dialogs with the Dialog API

    master

    Browser dialogs (alerts, confirms, and prompts) are dispatched by a Page via the page::Event::Dialog event.

    CRITICAL: Dialogs are dismissed automatically by default. If you attach a listener to the dialog event, you must manually call either accept() or dismiss() on the Dialog object. Failure to do so will cause the page to freeze, preventing subsequent actions like clicks from completing.

    Common dialog types include:

    • alert
    • beforeunload
    • confirm
    • prompt
    // Conceptual usage pattern based on the provided JS example
    // Note: The exact Rust syntax for event registration follows the Playwright-Rust pattern
    page.on(Event::Dialog, |dialog| {
        println!("{}", dialog.message()?);
        dialog.dismiss()?;
    });
  9. Handle Page events

    master

    The Page emits several events that can be monitored. Common events include:

    • Console(ConsoleMessage): Emitted when JS calls console.log, etc.
    • Dialog: Emitted when a JS dialog (alert, prompt, confirm) appears. Warning: You must handle these via Dialog.accept or Dialog.dismiss or the page will freeze.
    • Download(Download): Emitted when an attachment download starts.
    • Popup(Page): Emitted when a new tab/window is opened.
    • Request(Request) / Response(Response): Emitted during network activity.
    • Load: Emitted when the page has loaded.
  10. How Frame lifecycle and tree traversal work

    master

    A Frame represents a page or an iframe. The frame tree can be explored starting from the Page.main_frame() method.

    Tree Traversal:

    • Use child_frames() to get a list of all subframes within a frame.
    • Use parent_frame() to get the parent frame (returns None for the main frame or detached frames).
    • Use frame_element() to get the ElementHandle of the <iframe> or <frame> element that corresponds to this frame.

    Lifecycle Events: Frames are managed via events dispatched on the Page object:

    • frame_attached: Fired when a frame is attached to the page.
    • frame_navigated: Fired when the frame commits navigation to a different URL.
    • frame_detached: Fired when the frame is removed from the page.
    // Example: Dumping the frame tree
    async fn dump_frame_tree(frame: Frame, indent: &str) {
        println!("{}{}", indent, frame.url().unwrap());
        for child in frame.child_frames().unwrap() {
            dump_frame_tree(child, &format!("{}{}  ", indent, " ")).await;
        }
    }
  11. Understand the network request lifecycle in Playwright

    master

    When a page sends a request for a network resource, Playwright emits a specific sequence of events on the Page object:

    1. event: Page.request: Emitted when the request is first issued by the page.
    2. event: Page.response: Emitted when the response status and headers are received.
    3. event: Page.requestFinished: Emitted when the response body is fully downloaded and the request is complete.

    Error Handling & Redirects:

    • If a request fails, Playwright emits event: Page.requestFailed instead of requestfinished (and potentially instead of response).
    • HTTP Errors: Responses with HTTP error codes (like 404 or 503) are considered successful from an HTTP standpoint; they will trigger the requestfinished event.
    • Redirects: If a request receives a redirect response, the request is considered successfully finished (requestfinished), and a new request is issued to the redirected URL.