Playwright for Elixir

repository·main·Indexed 41 days ago

https://github.com/coreyti/playwright-elixir

A library for automating Chromium, Firefox, and WebKit using a single API for Elixir developers. It provides tools for cross-browser web automation, including browser instance management via `Playwright.launch/2` and `Playwright.connect/2`, isolated browser contexts, and integration with ExUnit through `PlaywrightTest.Case`. The library also supports low-level browser interaction via the Chrome DevTools Protocol (CDP) through `Playwright.CDPSession`.

Tokens
12.8K
Snippets
57
Records
69
Agent score
51%

What's inside Playwright for Elixir

  1. Install Playwright for Elixir

    main

    To install Playwright, add :playwright to your deps in mix.exs. After adding the dependency, you must run the installation task to download the necessary browser runtimes (Chromium, Firefox, and WebKit).

    1. Add to mix.exs:
    def deps do
      [
        {:playwright, "~> 1.18.0-alpha.1"}
      ]
    end
    1. Run the installation command in your terminal:
    mix playwright.install
  2. Use custom user agents with Phoenix Ecto Sandbox

    main

    When running Playwright tests that involve a database, you can use a custom user agent to help identify requests originating from specific test processes.

    To integrate with Phoenix.Ecto.SQL.Sandbox, set the userAgent to the value returned by Phoenix.Ecto.SQL.Sandbox.metadata_for(YourApp.Repo, pid). This allows the database sandbox to correctly associate the browser's requests with the appropriate Elixir process.

    # Example pattern for Ecto Sandbox integration
    userAgent = Phoenix.Ecto.SQL.Sandbox.metadata_for(YourApp.Repo, self())
    context = Browser.new_context(browser, %{"userAgent" => userAgent})
  3. Basic Usage of Playwright

    main

    You can use Playwright by aliasing or importing the Playwright module. The workflow typically involves launching a browser, creating a new page, navigating to a URL, and performing actions.

    Supported browsers are :chromium, :firefox, and :webkit.

    {:ok, browser} = Playwright.launch(:chromium)
    page =
      browser |> Playwright.Browser.new_page()
    
    page
      |> Playwright.Page.goto("http://example.com")
    
    page
      |> Playwright.Page.title()
      |> IO.puts()
    
    browser
      |> Playwright.Browser.close()
  4. What is an ElementHandle and when to use it

    main

    An ElementHandle represents a specific, in-page DOM element.

    ⚠️ DISCOURAGED

    Use of Playwright.ElementHandle is generally discouraged. You should prefer Playwright.Locator instances and web-first assertions instead.

    When to use ElementHandle

    You should only use ElementHandle if you need to retain a handle to a specific DOM node to pass it as an argument into Playwright.Page.evaluate/3 or Playwright.Page.eval_on_selector/5.

    ElementHandle vs. Locator

    • ElementHandle: Points to a particular element. If the DOM changes (e.g., the element is replaced by a new component), the handle still points to the old, potentially stale DOM node.
    • Playwright.Locator: Captures the logic of how to find an element. Every time a locator is used, it re-locates the element using the selector, ensuring it always interacts with the most up-to-date DOM node.
    # Example of ElementHandle (points to a specific node)
    handle = Page.q(page, "a")
    :ok = ElementHandle.click(handle)
    
    # Example of Locator (re-locates every time)
    locator = Page.locator("text=Submit")
    Locator.hover(locator)
    Locator.click(locator)
  5. Manage browser instances with Playwright.Browser

    main

    A Playwright.Browser instance is the top-level object used to control a browser session. It is typically created using Playwright.BrowserType.launch/0 (for the "driver" transport) or Playwright.BrowserType.connect/1 (for the "websocket" transport).

    Key capabilities include:

    • Creating new BrowserContext instances (incognito-like sessions).
    • Creating new Page instances.
    • Managing the lifecycle of the browser (closing/disconnecting).
    • Listing all active contexts.
    alias Playwright.{Browser, Page}
    
    {:ok, browser} = Playwright.launch(:chromium)
    page = Browser.new_page(browser)
    
    Page.goto(page, "https://example.com")
    Browser.close(browser)
  6. Use Playwright.JSHandle to interact with in-page JavaScript objects

    main

    A Playwright.JSHandle represents an in-page JavaScript object. You can create handles using Playwright.Page.evaluate_handle/3.

    Lifecycle and Memory Management:

    • JSHandle prevents the referenced JavaScript object from being garbage collected in the browser.
    • You must manually release the object by calling Playwright.JSHandle.dispose/1 to allow garbage collection.
    • Handles are automatically disposed if the origin frame navigates or the parent context is destroyed.

    Usage Patterns: JSHandle instances can be passed as arguments to:

    • Playwright.Page.eval_on_selector/5
    • Playwright.Page.evaluate/3
    • Playwright.Page.evaluate_handle/3
    handle = Page.evaluate_handle(page, "() => window")
  7. Using Chromium-based browsers (Chrome/Edge)

    main

    While Playwright works best with the version of Chromium it is bundled with, you can use the :executable_path option to control Google Chrome or Microsoft Edge.

    Warning: Use :executable_path with extreme caution as there is no guarantee of compatibility with non-bundled versions. For testing proprietary media codecs (like video playback), stock browsers like Google Chrome or Microsoft Edge are recommended.

  8. What are Locators and how to create them

    main

    Locators are the central piece of Playwright's auto-waiting and retry-ability. They represent a way to find element(s) on the page at any moment.

    Instances of Playwright.Locator can be created using:

    • Playwright.Locator.new/2 passing a Playwright.Frame.t() or Playwright.Page.t() and a selector string.
    • Playwright.Page.locator/2 (convenience function on the Page module).
    • Playwright.Frame.locator/2 (convenience function on the Frame module).
    # Using the new/2 constructor
    locator = Playwright.Locator.new(page, ".my-element")
    
    # Or using the Page convenience method
    locator = Playwright.Page.locator(page, ".my-element")
  9. Understand the Frame lifecycle and tree

    main

    A Frame represents a browsing context within a Playwright.Page. The frame tree can be accessed via Playwright.Page.main_frame/1 and Playwright.Frame.child_frames/1.

    A Frame instance lifecycle is governed by three specific events dispatched on the Playwright.Page resource:

    1. :frame_attached: Fired when the frame is attached to the page. A frame can only be attached once.
    2. :frame_navigated: Fired when the frame commits navigation to a different URL.
    3. :frame_detached: Fired when the frame is detached from the page. A frame can only be detached once.