playwright-ruby-client

repository·main·Indexed 19 days ago

https://github.com/yusukeiwaki/playwright-ruby-client

A Ruby client for Playwright that enables browser automation, web scraping, and mobile (Android) automation. It communicates with a Playwright server via Node.js/playwright-core to provide capabilities such as capturing screenshots, interacting with web elements using locators, and automating Android browsers and native apps.

Tokens
78.7K
Snippets
363
Records
440
Agent score
67%

What's inside playwright-ruby-client

  1. What is a JSHandle?

    main

    A JSHandle represents an in-page JavaScript object. You can create one using Page#evaluate_handle.

    Lifecycle and Memory Management:

    • JSHandle prevents the referenced JavaScript object from being garbage collected in the browser.
    • To release the reference manually, use JSHandle#dispose.
    • JSHandle instances are automatically disposed when the origin frame navigates or the parent context is destroyed.

    JSHandle instances can be passed as arguments to Page#eval_on_selector, Page#evaluate, and Page#evaluate_handle.

    window_handle = page.evaluate_handle("window")
    # ...
    window_handle.dispose
  2. How Worker objects and events work

    main

    The Worker class represents a WebWorker. You can interact with workers in two ways:

    1. Listening for new workers: The page object emits a worker event whenever a new worker is created. You can attach a listener to this event to capture the Worker instance.
    2. Monitoring worker lifecycle: The Worker object itself emits a close event when the worker is terminated or destroyed.

    You can also retrieve a list of all currently active workers using page.workers.

    def handle_worker(worker)
      puts "worker created: #{worker.url}"
      worker.once("close", -> (w) { puts "worker destroyed: #{w.url}" })
    end
    
    page.on('worker', method(:handle_worker))
    
    puts "current workers:"
    page.workers.each do |worker|
      puts "    #{worker.url}"
    end
  3. How BrowserContexts work

    main

    BrowserContexts allow you to operate multiple independent browser sessions. They provide isolation; for example, if a page opens a popup via window.open, that popup belongs to the same context as the parent page.

    Playwright supports creating isolated, non-persistent browser contexts using the Browser#new_context method. These non-persistent contexts do not write any browsing data to disk, making them ideal for incognito-like testing sessions.

    # create a new incognito browser context
    context = browser.new_context
    
    # create a new page inside context.
    page = context.new_page
    page.goto("https://example.com")
    
    # dispose context once it is no longer needed.
    context.close
  4. Manage a virtual keyboard with Keyboard API

    main

    The Keyboard API provides methods to simulate keyboard interactions.

    • High-level typing: Use Keyboard#type to send a sequence of keydown, keypress/input, and keyup events for each character. This is useful for pages with special keyboard handling.
    • Single key presses: Use Keyboard#press to trigger a single key or a combination of keys (e.g., Shift+A).
    • Fine-grained control: Use Keyboard#down and Keyboard#up to manually manage modifier keys (like Shift or Control) or to simulate holding a key down.
    • Fast text insertion: Use Keyboard#insert_text to dispatch only an input event without the overhead of keydown or keyup events.
    page.keyboard.type("Hello") # types instantly
    page.keyboard.type("World", delay: 100) # types slower, like a user
  5. Use web-first assertions vs standard assertions

    main

    Standard RSpec assertions (e.g., expect(locator.text_content).to include(...)) check the state of the element immediately. If the element's content is updated asynchronously (e.g., after an API call), these assertions may fail because they do not wait for the condition to be met.

    Web-first assertions (e.g., expect(locator).to have_text(...)) are provided by Playwright and automatically wait for the expected condition to be satisfied before failing. This makes tests more resilient to asynchronous UI updates.

    # Not web-first assertion (may fail if content loads asynchronously)
    expect(dashboard_container.text_content).to include('Hi, playwright!')
    
    # Web-first assertion (automatically waits for the text to appear)
    expect(dashboard_container).to have_text('Hi, playwright!')
  6. How APIRequestContext works and manages cookies

    main

    The APIRequestContext is used for Web API testing, such as triggering endpoints, configuring micro-services, or preparing environments for E2E tests.

    There are two ways to obtain an APIRequestContext:

    1. Shared Context: Access it via BrowserContext#request or Page#request. These instances share the same cookie jar as the browser context. If you make an API request, cookies are automatically set in the browser page and vice versa.
    2. Isolated Context: Create a standalone instance using APIRequest#new_context. This instance has its own isolated cookie storage and does not share cookies with any browser context.

    page.request is a shortcut for page.context().request and returns the same instance.

    playwright.chromium.launch do |browser|
      # Shared context example
      context = browser.new_context(base_url: 'https://api.github.com')
      api_request_context = context.request
    
      # The request above shares cookies with the 'context'
      response = api_request_context.post("/user/repos", data: { name: 'test-repo' })
    end
  7. Understand Playwright actionability and text rendering limitations

    main

    When migrating from Selenium to Playwright, be aware of these behavioral differences:

    • Actionability: Playwright will not click invisible elements or elements that are being moved. It waits for an element to become 'actionable'. If you click a disabled element, Playwright will wait for it to become enabled and eventually time out, rather than returning immediately.
    • Visible Text: Playwright uses the browser's innerText behavior. For example, a <br> element hidden with display: none will not result in a newline in the extracted text string.
    • Window Management: current_window.maximize and current_window.fullscreen only work in headful (non-headless) mode.
    • Drag and Drop: Capybara::Node::Element#drag_to does not support the html5 parameter because HTML5 drag and drop is not fully supported in Playwright.
  8. Access WebStorage via Page#local_storage and Page#session_storage

    main

    WebStorage provides an asynchronous, browser-consistent API to interact with a page's localStorage or sessionStorage for the current origin. You can access these storage instances through the Page#local_storage and Page#session_storage methods.

    page.goto("https://example.com")
    # Access localStorage
    page.local_storage.set_item("token", "abc")
    
    # Access sessionStorage
    page.session_storage.set_item("session_id", "123")
  9. Handle Page events

    main

    The Page class inherits from an event emitter, allowing you to listen for lifecycle events like load, request, or dialog. You can use standard methods like on, once, and off (or removeListener) to manage these subscriptions.

    Note: When using once, the callback receives the page object as an argument.

    # Listen for a single load event
    page.once("load", -> (page) { puts "page loaded!" })
    
    # Listen for all request events and then unsubscribe
    listener = -> (req) { puts "a request was made: #{req.url}" }
    page.on('request', listener)
    page.goto('https://example.com/')
    page.off('request', listener)
  10. Use Playwright-native scripting within Capybara

    main

    While most Capybara::Session and Capybara::Node::Element methods are available, you can access the underlying Playwright objects for more precise and efficient automation. This is useful for accessing Playwright-specific functions like waitForNavigation or waitForSelector that are not part of the standard Capybara DSL.

    • Use with_playwright_page on the driver to get a Playwright::Page instance.
    • Use with_playwright_element_handle on a Capybara element to get a Playwright::ElementHandle instance.
    # Access Playwright::Page
    Capybara.current_session.driver.with_playwright_page do |page|
      # `page` is an instance of Playwright::Page
      page.click('a[data-item-type="global_search"]')
    end
    
    # Access Playwright::ElementHandle
    all('.list-item').each do |li|
      li.with_playwright_element_handle do |handle|
        # `handle` is an instance of Playwright::ElementHandle
        handle.query_selector('a').text_content
      end
    end
  11. Use the Touchscreen class to emulate tap gestures

    main

    The Touchscreen class allows you to emulate tap gestures using main-frame CSS pixels relative to the top-left corner of the viewport.

    Prerequisite: Methods on the Touchscreen class can only be used in browser contexts that have been initialized with the has_touch option set to true. If you attempt to use these methods in a context where has_touch is false, the operation will throw an error.