Skip to main content

macOS GUI testing framework with background testing, sub-millisecond element access, and self-healing locators

Reason this release was yanked:

Deprecated. Use: cargo install axterminator --features cli

Project description

AXTerminator

PyPI version Downloads Tests Python macOS License Rust Discussions

macOS GUI testing framework with background testing, sub-millisecond element access, and self-healing locators.

Quick Start · Features · API · Examples · Docs · Benchmarks


Background Testing

Test macOS apps without stealing focus. AXTerminator uses the macOS Accessibility API (AXUIElement) to interact with applications in the background. Your active window stays focused while tests run behind it.

No other macOS GUI testing framework offers this -- XCUITest, Appium, PyAutoGUI, and Maestro all require the application under test to be in the foreground.

import axterminator as ax

# Tests run in the background -- your active window stays focused
calculator = ax.app(name="Calculator")
calculator.find("5").click()    # No focus stealing
calculator.find("+").click()    # Continue your work
calculator.find("3").click()    # Tests just work
calculator.find("=").click()

Quick Start

1. Install

pip install axterminator

2. Grant accessibility permissions

Open System Settings > Privacy & Security > Accessibility and add your terminal app (Terminal, iTerm2, VS Code, etc.).

3. Run your first test

import axterminator as ax

# Check permissions
if not ax.is_accessibility_enabled():
    print("Enable in System Settings > Privacy & Security > Accessibility")
    exit(1)

# Connect to any running app
app = ax.app(name="Calculator")

# Interact -- background mode by default
app.find("7").click()
app.find("+").click()
app.find("3").click()
app.find("=").click()

That's it. Three lines to connect and interact.

Why AXTerminator?

Capability AXTerminator XCUITest Appium PyAutoGUI Maestro
Background testing Yes No No No No
Element access 379 us ~200 ms ~500 ms ~100 ms ~300 ms
Cross-app testing Yes No Limited Yes Limited
Self-healing 7 strategies No Basic No Yes
AI vision fallback Yes No No No No
Python API Yes No Yes Yes No
No Xcode required Yes No No Yes Yes

Element access benchmarked on M1 MacBook Pro, macOS 14.2. See full benchmarks.

Features

Background Testing

# User continues working while tests run
for i in range(100):
    app.find("Refresh").click()  # All in background

Self-Healing Locators (7 Strategies)

# Element survives UI changes via fallback strategies:
# 1. data_testid  - Developer-set stable IDs
# 2. aria_label   - Accessibility labels
# 3. identifier   - AX identifier
# 4. title        - Element title (fuzzy matching)
# 5. xpath        - Structural path
# 6. position     - Relative position
# 7. visual_vlm   - AI vision fallback

AI Vision Detection (VLM)

# When all else fails, use AI to find elements visually
ax.configure_vlm(backend="mlx")      # Local (fast, private)
ax.configure_vlm(backend="anthropic") # Claude Vision
ax.configure_vlm(backend="openai")    # OpenAI Vision
ax.configure_vlm(backend="gemini")    # Gemini Vision
ax.configure_vlm(backend="ollama")    # Local Ollama

# Natural language element description
app.find("the blue Save button in the toolbar")

pytest Integration

import pytest

@pytest.mark.ax_requires_app("Calculator")
def test_addition(ax_app, ax_wait):
    app = ax_app("Calculator")
    app.find("7").click()
    app.find("+").click()
    app.find("3").click()
    app.find("=").click()
    ax_wait(0.1)

Recording Mode

from axterminator import Recorder

recorder = Recorder(app)
recorder.start()
# ... perform actions ...
recorder.stop()

# Generate test code
print(recorder.generate_test())

API Reference

App Connection

# By name
app = ax.app(name="Safari")

# By bundle ID (recommended -- locale-independent)
app = ax.app(bundle_id="com.apple.Safari")

# By PID
app = ax.app(pid=12345)

# Launch if not running
app = ax.app(name="Notes", launch=True)

Finding Elements

# By text/title
button = app.find("Save")

# With timeout
button = app.find("Save", timeout_ms=5000)

# By role
text_field = app.find("", role="AXTextField")

# Find all matching
buttons = app.find_all("role:AXButton")

Actions

# Clicks (background by default)
element.click()
element.double_click()
element.right_click()

# Focused mode (for text input)
element.click(mode=ax.FOCUS)
element.type_text("Hello World!")

# Get properties
print(element.title)
print(element.value)
print(element.role)

Synchronization

from axterminator.sync import wait_for_idle, wait_for_element

# Wait for app to settle
wait_for_idle(app, timeout_ms=5000)

# Wait for element to appear
button = wait_for_element(app, "Done", timeout_ms=3000)

Performance

Measured on Apple M1 MacBook Pro, macOS 14.2, using Criterion benchmarks against Finder.app:

Operation Time Method
Single attribute read 54 us Criterion (get_ax_role)
Element access (window -> child) 379 us Criterion (search_first_button)
Perform action 20 us Criterion (perform_action_overhead)
Find element (Python, incl. overhead) ~0.5-1 ms time.perf_counter() loop

The speedup over Appium (~500 ms per element access) comes from eliminating HTTP/WebDriver/JSON overhead. AXTerminator calls the Accessibility API directly: Python -> PyO3 FFI -> Rust -> AXUIElement.

Reproduce: cargo bench or compile and run benches/bench_quick.rs. See full benchmarks.

Examples

See examples/ for real-world automation:

Script Description
basic_usage.py Calculator automation
system_preferences.py System Settings navigation
finder_automation.py Finder file operations
notes_app.py Notes app automation
textedit_automation.py Document creation
pytest_example.py pytest integration
self_healing_locators.py Locator strategies
vlm_visual_detection.py VLM fallback demo

Browser Extension

Record browser interactions and generate axterminator code:

  1. Load browser-extension/ in Chrome (Developer mode)
  2. Click extension icon, then Start Recording
  3. Interact with web pages
  4. Copy generated Python code

Installation Options

# Basic
pip install axterminator

# With VLM backends
pip install axterminator[vlm]           # Local MLX
pip install axterminator[vlm-anthropic] # Claude Vision
pip install axterminator[vlm-openai]    # OpenAI Vision
pip install axterminator[vlm-gemini]    # Gemini Vision
pip install axterminator[vlm-ollama]    # Ollama
pip install axterminator[vlm-all]       # All backends

Requirements

  • macOS 12+ (Monterey or later)
  • Python 3.9+
  • Accessibility permissions granted to terminal/IDE

Building from Source

git clone https://github.com/MikkoParkkola/axterminator
cd axterminator

# Install Rust (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Build
pip install maturin
maturin develop

# Run tests
cargo test
pytest python/tests/

Community

Acknowledgements

AXTerminator was inspired by Terminator by mediar-ai, which pioneered accessible desktop GUI automation on Windows. AXTerminator brings similar capabilities to macOS, with the addition of background testing -- the ability to test applications without stealing window focus -- made possible by leveraging undocumented behavior of Apple's Accessibility API.

License

MIT OR Apache-2.0


Built with Rust + Python | Report Bug · Request Feature

Project details


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

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

axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl (420.6 kB view details)

Uploaded CPython 3.9+macOS 11.0+ ARM64

axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl (433.5 kB view details)

Uploaded CPython 3.9+macOS 10.12+ x86-64

File details

Details for the file axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 5df1e54f30f0ae7c0bc5557e28e4ad945c303f017567077ba21778051fc203fd
MD5 2c55af815494d8e2502003c8021191fd
BLAKE2b-256 7ada5f83dc4e6f293a44acaf7e9ac5e19e191ffbf65ba35106588bb58521d33a

See more details on using hashes here.

Provenance

The following attestation bundles were made for axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on MikkoParkkola/axterminator

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

File details

Details for the file axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 1b909f1942372d2961e3c0ecf58b5e593cf88fbb63aa18ec08fa8ded144b100d
MD5 d2983d95255e2c70c61e1dabac0bc4db
BLAKE2b-256 9077350b39ee2404e8213f08ea9d6492a789832d46b8c1be5609579ffde19b79

See more details on using hashes here.

Provenance

The following attestation bundles were made for axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on MikkoParkkola/axterminator

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