Skip to main content

Galaxy Selenium interaction framework

Project description

https://badge.fury.io/py/galaxy-selenium.svg

Galaxy Browser Automation Framework

Overview

This package provides a browser automation framework for Galaxy with:

  • Multi-backend support: Selenium and Playwright backends with a unified API

  • Protocol-based architecture: Clean separation between Galaxy logic and browser interaction

  • Comprehensive testing: Full unit test coverage for both backends

  • CLI tooling: Utilities for building command-line automation scripts

  • Type safety: Full type hints with mypy validation

  • Code: https://github.com/galaxyproject/galaxy

Architecture

Core Interfaces

The framework is built around two main protocol interfaces:

HasDriverProtocol (has_driver_protocol.py)

Defines ~60+ browser automation operations including:

  • Element finding (by ID, selector, XPath, etc.)

  • Wait methods (visible, clickable, absent, etc.)

  • Interactions (click, hover, drag-and-drop, keyboard input)

  • Navigation (URLs, frames, alerts)

  • JavaScript execution

  • Accessibility testing (axe-core integration)

WebElementProtocol (web_element_protocol.py)

Defines the common element API:

  • text, click(), send_keys(), clear()

  • get_attribute(), is_displayed(), is_enabled()

  • find_element(), find_elements()

  • value_of_css_property() for CSS introspection

Architecture Diagram

┌─────────────────────────────────────────────────────┐
│         Galaxy Application Layer                     │
│                                                       │
│  NavigatesGalaxy (navigates_galaxy.py)              │
│  - Galaxy-specific page objects                      │
│  - Workflow interactions                             │
│  - History management                                │
│  - Tool execution                                    │
└────────────────┬────────────────────────────────────┘
                 │ extends
                 ↓
┌─────────────────────────────────────────────────────┐
│      Browser Automation Abstraction Layer           │
│                                                       │
│  HasDriverProxy (has_driver_proxy.py)               │
│  - Delegates to HasDriverProtocol implementation     │
│  - Runtime backend selection via composition         │
└────────────────┬────────────────────────────────────┘
                 │ uses
                 ↓
┌──────────────────────────────────┬──────────────────┐
│   HasDriverProtocol              │                  │
│   (has_driver_protocol.py)       │                  │
│   - Abstract interface           │                  │
│   - ~60+ operations              │                  │
└──────────────┬───────────────────┴──────────────────┘
               │ implemented by
     ┌─────────┴──────────┐
     ↓                    ↓
┌─────────────┐    ┌──────────────────┐
│  HasDriver  │    │ HasPlaywright    │
│             │    │ Driver           │
│ Selenium    │    │                  │
│ backend     │    │ Playwright       │
│             │    │ backend          │
└──────┬──────┘    └────────┬─────────┘
       │                    │
       ↓                    ↓
┌─────────────┐    ┌──────────────────┐
│ WebElement  │    │ PlaywrightElement│
│ (Selenium)  │    │ (wrapper)        │
│             │    │                  │
│ implements  │    │ implements       │
│ protocol    │    │ protocol         │
└─────────────┘    └──────────────────┘
       │                    │
       └──────────┬─────────┘
                  ↓
       WebElementProtocol
       (web_element_protocol.py)

Implementations

Selenium Backend (has_driver.py)
  • Uses Selenium WebDriver

  • Direct WebElement support (implements protocol natively)

  • Mature, widely-used automation framework

  • Supports remote execution (Selenium Grid)

Playwright Backend (has_playwright_driver.py)
  • Uses Playwright sync API

  • PlaywrightElement wrapper (adapts ElementHandle to protocol)

  • Modern automation with auto-waiting

  • Fast and reliable for modern web apps

  • Local execution only (no remote support)

Both implementations:

  • Share identical test suite (150+ parametrized tests)

  • Provide consistent API via protocols

  • Support headless and headed modes

  • Include full type hints

Separation of Concerns

Application Logic          │  Browser Automation
(Galaxy-specific)         │  (Generic, reusable)
─────────────────────────────────────────────────
navigates_galaxy.py       │  has_driver_protocol.py
- Galaxy UI interactions  │  - Abstract interface
- Workflow automation     │  - Element finding
- History operations      │  - Wait strategies
- Tool wrappers           │  - Interactions
                          │
smart_components.py       │  has_driver.py
- Galaxy component        │  - Selenium impl
  wrappers                │
- Page object patterns    │  has_playwright_driver.py
                          │  - Playwright impl
                          │
                          │  web_element_protocol.py
                          │  - Element interface

Key Principle: Galaxy-specific logic lives in navigates_galaxy.py and extends the generic browser automation protocols. This separation allows:

  • Testing browser automation independently

  • Reusing automation primitives across projects

  • Switching backends without changing application code

  • Clear boundaries between concerns

Testing

The framework includes comprehensive unit tests in test/unit/selenium/test_has_driver.py:

  • Parametrized tests: Every test runs against Selenium, Playwright, and proxied backends

  • 150+ test cases covering all protocol methods

  • Test fixtures: Reusable HTML pages served via local HTTP server

  • Scope-optimized: Session-scoped server, function-scoped drivers

Run tests from the package directory:

# All tests
uv run pytest tests/seleniumtests/test_has_driver.py -v

# Specific test class
uv run pytest tests/seleniumtests/test_has_driver.py::TestElementFinding -v

# Type checking
make mypy

Building CLI Tools

The cli.py module provides infrastructure for building command-line automation tools.

Core Components

add_selenium_arguments(parser)

Adds standard CLI arguments:

  • --selenium-browser: Browser choice (chrome, firefox, auto)

  • --selenium-headless: Headless mode flag

  • --backend: Backend selection (selenium, playwright)

  • --galaxy_url: Target Galaxy instance URL

  • --selenium-remote: Remote execution (Selenium only)

DriverWrapper

Adapts argparse args to a NavigatesGalaxy instance:

  • Handles backend selection

  • Manages virtual display (for headless Selenium)

  • Provides Galaxy navigation utilities

  • Cleanup via finish() method

Example: dump_tour.py

The scripts/dump_tour.py script demonstrates CLI tool development:

#!/usr/bin/env python
import argparse
from galaxy.selenium import cli

def main(argv=None):
    args = _arg_parser().parse_args(argv)
    driver_wrapper = cli.DriverWrapper(args)

    # Use driver_wrapper for automation
    callback = DumpTourCallback(driver_wrapper, args.output)
    driver_wrapper.run_tour(args.tour, tour_callback=callback)

def _arg_parser():
    parser = argparse.ArgumentParser(description="Walk a Galaxy tour")
    parser.add_argument("tour", help="tour to walk")
    parser.add_argument("-o", "--output", help="screenshot output dir")
    parser = cli.add_selenium_arguments(parser)  # Add standard args
    return parser

class DumpTourCallback:
    def handle_step(self, step, step_index: int):
        self.driver_wrapper.save_screenshot(f"{output}/{step_index}.png")

Usage:

# With Selenium backend
python dump_tour.py my_tour.yaml --backend selenium --selenium-headless

# With Playwright backend
python dump_tour.py my_tour.yaml --backend playwright --galaxy_url http://localhost:8080

Building Your Own CLI Tool

  1. Import the CLI utilities:

    from galaxy.selenium import cli
  2. Create argument parser:

    parser = argparse.ArgumentParser(description="My automation tool")
    parser.add_argument("--my-option", help="Custom option")
    parser = cli.add_selenium_arguments(parser)  # Add standard args
  3. Create DriverWrapper:

    args = parser.parse_args()
    driver_wrapper = cli.DriverWrapper(args)
  4. Use NavigatesGalaxy API:

    # driver_wrapper has all NavigatesGalaxy methods
    driver_wrapper.navigate_to(url)
    driver_wrapper.click_selector("#my-button")
    driver_wrapper.wait_for_selector_visible(".result")
  5. Clean up:

    driver_wrapper.finish()  # Quits driver and stops virtual display

Development

Package Structure

packages/selenium/
├── galaxy/selenium/          # Symlink to lib/galaxy/selenium/
├── tests/seleniumtests/      # Symlink to test/unit/selenium/
├── README.rst                # This file
└── pyproject.toml

lib/galaxy/selenium/          # Actual source code
├── has_driver_protocol.py    # Protocol interface
├── has_driver.py             # Selenium implementation
├── has_playwright_driver.py  # Playwright implementation
├── web_element_protocol.py   # Element interface
├── playwright_element.py     # Element wrapper
├── navigates_galaxy.py       # Galaxy-specific logic
├── smart_components.py       # Component wrappers
├── cli.py                    # CLI utilities
└── scripts/
    └── dump_tour.py          # Example CLI tool

test/unit/selenium/           # Actual tests
├── conftest.py               # Pytest fixtures
├── test_has_driver.py        # Main test suite
└── fixtures/                 # HTML test pages

Running Commands

Always use uv run from the package directory:

# Run tests
uv run pytest tests/seleniumtests/test_has_driver.py -v

# Type checking
make mypy

# Linting
uv run ruff check .

Adding New Low-Level Browser Operations

Low-level operations are generic browser automation primitives that work with both backends.

Manual Process:

  1. Add to protocol (has_driver_protocol.py):

    @abstractmethod
    def my_new_operation(self, param: str) -> bool:
        """Do something useful."""
        ...
  2. Implement in Selenium (has_driver.py):

    def my_new_operation(self, param: str) -> bool:
        # Selenium implementation
        return self.driver.do_something(param)
  3. Implement in Playwright (has_playwright_driver.py):

    def my_new_operation(self, param: str) -> bool:
        # Playwright implementation
        return self.page.do_something(param)
  4. Update proxy (has_driver_proxy.py):

    def my_new_operation(self, param: str) -> bool:
        """Do something useful."""
        return self._driver_impl.my_new_operation(param)
  5. Add tests (test_has_driver.py):

    def test_my_new_operation(self, has_driver_instance, base_url):
        """Test new operation works on both backends."""
        has_driver_instance.navigate_to(f"{base_url}/test.html")
        result = has_driver_instance.my_new_operation("test")
        assert result is True

Automated Process:

Use the /add-browser-operation slash command to automate these steps:

/add-browser-operation scroll element to center of viewport

This command will generate all the necessary code across all files and create tests.

Adding New Smart Component Operations

Smart component operations are higher-level conveniences that may require adding low-level operations first. These operations make Galaxy components more ergonomic to use in tests.

When to Add Smart Operations:

  • When you find yourself repeating a pattern of low-level operations

  • When Galaxy-specific UI patterns need convenient wrappers

  • When you want to express test intent more clearly

Process:

  1. Determine if low-level support exists:

    Check if the required browser operation exists in HasDriverProtocol. If not, add it first using the process above (or /add-browser-operation).

  2. Add to SmartTarget (smart_components.py):

    Add a method to the SmartTarget class:

    def my_smart_operation(self, **kwds):
        """High-level operation description."""
        # Delegate to has_driver protocol methods
        element = self._has_driver.wait_for_visible(self._target, **kwds)
        return self._has_driver.my_low_level_operation(element)
  3. Consider return value wrapping:

    If your operation returns a Component or Target, wrap it:

    def get_child_component(self, name: str):
        """Get a child component and wrap it smartly."""
        child = self._target.get_child(name)
        return self._wrap(child)  # Returns SmartTarget
  4. Add tests (test/unit/selenium/test_smart_components.py):

    Test with both backends using the has_driver_instance fixture:

    def test_my_smart_operation(self, has_driver_instance, base_url):
        has_driver_instance.navigate_to(f"{base_url}/smart_components.html")
    
        # Use SmartComponent wrapping
        component = SmartComponent(MyComponent(), has_driver_instance)
        result = component.my_target.my_smart_operation()
    
        assert result is not None

Example: Adding a “wait_for_and_hover” operation

This demonstrates when you need to add a low-level operation first:

  1. Check low-level support: hover() already exists in HasDriverProtocol

  2. Add to SmartTarget:

    def wait_for_and_hover(self, **kwds):
        """Wait for element to be visible and hover over it."""
        element = self._has_driver.wait_for_visible(self._target, **kwds)
        self._has_driver.hover(element)
        return element
  3. Usage in tests:

    # Before: Multiple steps
    element = driver.wait_for_visible(component.menu)
    driver.hover(element)
    
    # After: One expressive call
    component.menu.wait_for_and_hover()

Example: Adding operation requiring new low-level support

When the operation needs a new browser primitive:

  1. Add low-level operation (see “Adding New Low-Level Browser Operations”):

    Add scroll_to_center(element) to protocols and implementations

  2. Add smart wrapper:

    def wait_for_and_scroll_to_center(self, **kwds):
        """Wait for element and scroll it to viewport center."""
        element = self._has_driver.wait_for_visible(self._target, **kwds)
        self._has_driver.scroll_to_center(element)
        return element
  3. Test both levels:

    • Test low-level in test_has_driver.py

    • Test smart wrapper in test_smart_components.py

See Also

History

26.0.1.dev0

26.0.0 (2026-04-08)

Bug fixes

Enhancements

25.1.2 (2026-03-09)

No recorded changes since last release

25.1.1 (2026-02-03)

Bug fixes

25.1.0 (2025-12-12)

Bug fixes

  • Fix transient selenium error when adding collection input. by @jmchilton in #20460

Enhancements

25.0.4 (2025-11-18)

No recorded changes since last release

25.0.3 (2025-09-23)

No recorded changes since last release

25.0.2 (2025-08-13)

No recorded changes since last release

25.0.1 (2025-06-20)

No recorded changes since last release

25.0.0 (2025-06-18)

Bug fixes

Enhancements

24.2.4 (2025-06-17)

Bug fixes

24.2.3 (2025-03-16)

No recorded changes since last release

24.2.2 (2025-03-08)

No recorded changes since last release

24.2.1 (2025-02-28)

Bug fixes

  • Set content-type to text/plain if dataset not safe by @mvdbeek in #19563

24.2.0 (2025-02-11)

Bug fixes

Enhancements

24.1.4 (2024-12-11)

Bug fixes

24.1.3 (2024-10-25)

No recorded changes since last release

24.1.2 (2024-09-25)

No recorded changes since last release

24.1.1 (2024-07-02)

Bug fixes

Enhancements

24.0.3 (2024-06-28)

No recorded changes since last release

24.0.2 (2024-05-07)

No recorded changes since last release

24.0.1 (2024-05-02)

Bug fixes

  • Set from_tool_form: true when saving new workflow by @mvdbeek in #17972

24.0.0 (2024-04-02)

Bug fixes

Enhancements

23.2.1 (2024-02-21)

Enhancements

23.1.4 (2024-01-04)

No recorded changes since last release

23.1.3 (2023-12-01)

No recorded changes since last release

23.1.2 (2023-11-29)

No recorded changes since last release

23.1.1 (2023-10-23)

Bug fixes

Enhancements

23.0.6 (2023-10-23)

No recorded changes since last release

23.0.5 (2023-07-29)

No recorded changes since last release

23.0.4 (2023-06-30)

No recorded changes since last release

23.0.3 (2023-06-26)

No recorded changes since last release

23.0.2 (2023-06-13)

Enhancements

  • Port selenium setup to non-deprecated selenium options by @mvdbeek in #16215

23.0.1 (2023-06-08)

Enhancements

  • Add support for launching workflows via Tutorial Mode by @hexylena in #15684

20.9.0 (2020-10-15)

  • First release from the 20.09 branch of Galaxy.

20.5.0 (2020-07-04)

  • First release from the 20.05 branch of Galaxy.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

galaxy_selenium-26.0.1.dev0.tar.gz (84.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

galaxy_selenium-26.0.1.dev0-py3-none-any.whl (78.8 kB view details)

Uploaded Python 3

File details

Details for the file galaxy_selenium-26.0.1.dev0.tar.gz.

File metadata

  • Download URL: galaxy_selenium-26.0.1.dev0.tar.gz
  • Upload date:
  • Size: 84.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for galaxy_selenium-26.0.1.dev0.tar.gz
Algorithm Hash digest
SHA256 89a4845217f32809bffb850306cfe7dd345a5f97dec24309ca3179ecfeba45f5
MD5 62051bf12a6d4b7899ddb10f8a534a73
BLAKE2b-256 287fafa4beb04480f4df5fd35dd4ab604ea4a5d19c1c11a62b8a42a261a4046f

See more details on using hashes here.

Provenance

The following attestation bundles were made for galaxy_selenium-26.0.1.dev0.tar.gz:

Publisher: publish_artifacts.yaml on galaxyproject/galaxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file galaxy_selenium-26.0.1.dev0-py3-none-any.whl.

File metadata

File hashes

Hashes for galaxy_selenium-26.0.1.dev0-py3-none-any.whl
Algorithm Hash digest
SHA256 4bd16c749a245234a4388b8e4995a5a3f69c731f089831a865640645b37e3d61
MD5 7daa476e9b969506ee49e2f888adb0b1
BLAKE2b-256 14c3eafc42c29c35847ea827d291fed505e803d5befe0f77fff693962e69728f

See more details on using hashes here.

Provenance

The following attestation bundles were made for galaxy_selenium-26.0.1.dev0-py3-none-any.whl:

Publisher: publish_artifacts.yaml on galaxyproject/galaxy

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page