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
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:
- Load
browser-extension/in Chrome (Developer mode) - Click extension icon, then Start Recording
- Interact with web pages
- 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
- GitHub Discussions -- Questions, feature requests, and show-and-tell
- Issue Tracker -- Bug reports
- Contributing Guide -- How to contribute
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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 420.6 kB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5df1e54f30f0ae7c0bc5557e28e4ad945c303f017567077ba21778051fc203fd
|
|
| MD5 |
2c55af815494d8e2502003c8021191fd
|
|
| BLAKE2b-256 |
7ada5f83dc4e6f293a44acaf7e9ac5e19e191ffbf65ba35106588bb58521d33a
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axterminator-0.4.0-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
5df1e54f30f0ae7c0bc5557e28e4ad945c303f017567077ba21778051fc203fd - Sigstore transparency entry: 1127768765
- Sigstore integration time:
-
Permalink:
MikkoParkkola/axterminator@388f241320a7aadedae4959fc6060bb28f6f0cbb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/MikkoParkkola
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@388f241320a7aadedae4959fc6060bb28f6f0cbb -
Trigger Event:
push
-
Statement type:
File details
Details for the file axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl.
File metadata
- Download URL: axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl
- Upload date:
- Size: 433.5 kB
- Tags: CPython 3.9+, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b909f1942372d2961e3c0ecf58b5e593cf88fbb63aa18ec08fa8ded144b100d
|
|
| MD5 |
d2983d95255e2c70c61e1dabac0bc4db
|
|
| BLAKE2b-256 |
9077350b39ee2404e8213f08ea9d6492a789832d46b8c1be5609579ffde19b79
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axterminator-0.4.0-cp39-abi3-macosx_10_12_x86_64.whl -
Subject digest:
1b909f1942372d2961e3c0ecf58b5e593cf88fbb63aa18ec08fa8ded144b100d - Sigstore transparency entry: 1127768798
- Sigstore integration time:
-
Permalink:
MikkoParkkola/axterminator@388f241320a7aadedae4959fc6060bb28f6f0cbb -
Branch / Tag:
refs/tags/v0.4.0 - Owner: https://github.com/MikkoParkkola
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@388f241320a7aadedae4959fc6060bb28f6f0cbb -
Trigger Event:
push
-
Statement type: