magogi/prod/: mgf-uitest-desktop-0.1.2 metadata and description
Shared observable-behavior UI testing for the Magogi PySide6/Qt desktop apps — drives each app's real widget tree in an offscreen QApplication and asserts the pillars an in-process handler call can't see: layout integrity (nothing squeezed below its own minimum, no elided text, no child escaping its parent), accessibility (accessible-name coverage + focus-chain reachability), runtime health (no QtWarning/QtCritical, no unraisable exception), plus real input events, modal-dialog auto-answer and a screenshot gallery, across a viewport x theme matrix. The desktop half of the mgf-uitest family (web sibling: mgf-uitest-web). Sibling under the mgf.* namespace.
| author | Bassam Alsanie, mgf-uitest-desktop contributors |
| classifiers |
|
| description_content_type | text/markdown |
| keywords | accessibility,desktop,magogi,pyside6,pytest-qt,qt,ui-testing |
| license | Apache-2.0 |
| license_file |
|
| metadata_version | 2.4 |
| project_urls |
|
| requires_dist |
|
| requires_python | >=3.11 |
Because this project isn't in the mirror_whitelist,
no releases from root/pypi are included.
| File | Tox results | History |
|---|---|---|
mgf_uitest_desktop-0.1.2-py3-none-any.whl
|
|
mgf-uitest-desktop
Observable-behavior UI testing for the Magogi PySide6/Qt desktop apps — the missing tier.
Every Magogi desktop app is a PySide6 application whose GUI suite, today, calls its handlers directly:
window._retry_connection() # the current shape
assert window._status_label.text() == "Retrying…"
That proves the handler works. It proves nothing about the button. Disconnect
retry_button.clicked and the suite stays green — which is not a hypothetical:
hrb-app shipped a visibly-broken button past a green suite on 2026-07-16, and the
audit that followed found zero real input events in the whole suite, four
objectNames, and no setAccessibleName anywhere.
This package closes that gap. It drives each app's real widget tree in an
offscreen QApplication, with real posted input events, and asserts the
pillars a handler call cannot see.
def test_greeting(console):
console.click("#greet-button") # a real mouse event
assert "Hello, world" in console.text("#greeting")
console.assert_ok() # layout + a11y + health
console.snapshot("greeting")
§1. The pillars
- Layout integrity — reads Qt's real geometry to catch what an operator
notices: text elided because
QFontMetricssays it never fitted, a child whose rect escapes its parent, a window squeezed below its ownminimumSizeHint(). Deterministic; no image baseline to bless. - Functional — drive the real golden paths through real input events, and assert the observable outcome.
- Accessibility — every interactive widget has an accessible name and is reachable by Tab. Honest scope: this is not axe — see §4.
- Runtime health — zero Qt warnings/criticals, zero unraisable exceptions. Qt fails quietly; this is what makes it audible.
- Responsive — the above across a viewport × theme (× DPI scale) matrix.
- Visual gallery — PNGs per matrix cell, for human eyeballing.
Plus the two things a Qt suite cannot do without: bounded waits instead of
wall-clock sleeps, and modal-dialog auto-answer — because
QMessageBox.exec() blocks inside C++, so a click on a button that asks "are you
sure?" does not fail a test today, it hangs it. That is why the fleet's
destructive verbs are untested, and expect_dialog is the way in.
§2. How it drives an app
In-process, through the app's own window factory. The consumer provides exactly
one fixture — app_launch, returning an AppLaunch that says how to build
its real top-level window — and the plugin supplies the rest:
app_launch → ui_app (offscreen QApplication, session) → ui_window
(built · sized · themed · exposed · ready) → console
The harness never imports mgf-hrb, mgf-common, mgf-brand-qt, or any app's
domain stack — mechanically pinned by an import-linter contract. Its runtime
dependencies are PySide6, pytest and pytest-qt, and nothing mgf-* at
all.
§3. Family
The desktop half of the mgf-uitest family. mgf-uitest-web is the web half, and
the shared vocabulary is deliberate: same fixture name (console), same
*_launch override contract, same seven pillar names, same describe()-as-
assertion-message discipline, same {slug}__{cell}.png gallery convention. The
two share names, not code — PUBLIC_API.md §5 maps every name web→desktop and
records where the two genuinely differ. Genuinely shared plumbing gets extracted
into an mgf-uitest-core only if a third consumer ever needs it — not on
speculation.
§4. What this is not — read before trusting a green run
assert_accessibleis not axe. The web sibling runs axe-core: ~90 audited WCAG rules maintained by Deque. Qt has no axe, and this library vendors no rule corpus. It checks two structural properties — every interactive control has an accessible name, and every one is reachable by Tab — which is the floor below which assistive technology sees an unlabelled blank. Passing it is not a WCAG claim. Colour contrast is absent on purpose: it belongs with the design tokens that define the colours (mgf-brand-qt), not with the harness that drives widgets.- There is no pixel baseline. Screenshots are a gallery for human eyes, the
same stance as the web half.
grab_differsis a same-process pixel delta probe ("did this repaint at all"), not a blessed-baseline diff. - Full boundary:
SCOPE.md.
§5. Setup
uv venv --python 3.13
uv pip install -e ".[dev]"
Qt needs no browser install, but it does need a platform plugin: the pytest plugin
sets QT_QPA_PLATFORM=offscreen for you (with setdefault, so
QT_QPA_PLATFORM=xcb pytest -k clipping still shows you the window when you are
debugging a finding).
§6. Running the suite
pytest --cov --cov-report=term-missing
Run the gates unpiped and read the real exit code. This is not style: a Qt
suite can print 61 passed and then die of SIGSEGV, and pytest | tail reports
the pipe's exit status, turning 139 into 0. That is how a real intermittent
segfault stayed invisible in this fleet for weeks
(LESSON_LEARNED.md).
Screenshots land in tests/_artifacts/gallery/
({name}__{viewport}__{theme}__{scale}x.png). See tests/integration/conftest.py
for a worked app_launch.
The app specs live in the apps, not here. This repo's own suite proves the library against a tiny in-repo sample window — including a test that seeds the sample's button unwired and asserts the gate fires. A harness that claims to catch dead buttons should have to prove it on one.
§7. Standards conformance
mgf-uitest-desktop declares L2 — Standard (CFM-02): conform to every MUST
rule, evidenced by a per-rule audit —
docs/inprogress/MGF_STANDARDS_CONFORMANCE.md.
As a 0.1.0 founding, that ledger is declared provisional: the mechanical gates
are green and the DOC-01 root set is complete, but the per-rule fresh-eyes audit
across the full corpus has not been run yet, and the ledger says so rather than
claiming a number it has not earned.
The public contract is PUBLIC_API.md (every name, its stability
tier); the boundary is SCOPE.md.