@devant-net/maestro-reporter
v0.1.5
Published
Streams Maestro test runs into devq-cloud — reads the JUnit XML report Maestro emits with --format junit, plus the screenshots/video/logs from --test-output-dir.
Downloads
599
Maintainers
Readme
@devant-net/maestro-reporter
Streams Maestro test runs into Devant Cloud.
Maestro doesn't expose a programmatic reporter API, so this package consumes
Maestro's own JUnit XML output (--format junit) plus the screenshot / video /
log directory it writes (--test-output-dir, --debug-output).
Install
bun add -D @devant-net/maestro-reporter
# or
npm i -D @devant-net/maestro-reporterUse
# 1. Run Maestro and tell it to emit JUnit + keep its artifact dirs.
maestro test \
--format junit \
--output build/maestro-report.xml \
--test-output-dir build/maestro-results \
--debug-output build/maestro-debug \
./flows
# 2. Upload to Devant Cloud.
devq-maestro-upload \
--junit build/maestro-report.xml \
--test-output-dir build/maestro-results \
--debug-output build/maestro-debug \
--run-name "PR #${{ github.event.number }}"The uploader exits 0 whenever the upload itself succeeds — it does not re-fail on test failures. Maestro's own exit code already encodes that, so your CI step can treat them independently.
Connecting to Devant Cloud
Either pass flags inline (--api-url, --api-token, --project-id) or set
the same env vars our other reporters read:
| Var | Default | Notes |
|-------------------|--------------------------|------------------------------------------------|
| DEVQ_API_URL | http://localhost:32124 | Your tenant's URL. |
| DEVQ_TOKEN | dev-admin-token | CI token (dq_ci_…) from Settings → CI/CD. |
| DEVQ_PROJECT_ID | 1 | Numeric project id. |
| DEVQ_RUN_NAME | Maestro — <ISO date> | Display name on the run. |
| DEVQ_RUN_ID | unset | If set, skip create/complete (orchestration). |
How tests bind to test cases
The reporter resolves each Maestro flow against a Devant Cloud test_case row
in this order:
<property name="devq-key">DEF-AB12</property>— explicit override. Maestro supports custom properties on flows.<property name="testCaseId">…</property>orxray-test-key— common conventions; only used if the value matches the[A-Z]+-[A-Z0-9]+shape.@KEYtoken anywhere in the flowname,classname, or property values.- Exact name match in the project, then auto-create. The reporter
prints the new key:
[devq] minted DEF-XYZ9 for "Login Flow" — add '@DEF-XYZ9' to the flow's name or a property to bind it
In Maestro YAML you bind a key with the name: field at the top of the flow:
name: Login Flow @DEF-XYZ9
appId: com.example.app
---
- launchApp
- tapOn: "Sign in"…or as a property:
name: Login Flow
appId: com.example.app
---
- launchApp
- tapOn: "Sign in"
# Properties live in a separate `# tags:`, `# properties:` section under the
# flow header — see the Maestro docs for the exact syntax.Status mapping
Maestro emits a non-standard status= attribute on each <testcase>:
| Maestro status | Devant Cloud status | Notes |
|------------------|---------------------|--------------------------------|
| SUCCESS | pass | |
| WARNING | pass | Soft fail — flow ran but the agent flagged a concern. |
| ERROR | fail | |
| CANCELED | blocked | Run aborted before this flow finished. |
| STOPPED | blocked | |
For frameworks that emit standard JUnit (<failure>, <error>, <skipped/>)
the reporter also handles those — same parser is used by other Devant Cloud
JUnit consumers.
What gets uploaded
Per Maestro flow (<testcase>):
| Source | Maps to in Devant Cloud |
|--------------------------------|----------------------------------------|
| <testcase status="..." time="..."> | One test_result + one test_result_attempt |
| <failure message="..." /> | error_message + error_stack |
| <system-out> / <system-err> | stdout / stderr on the attempt |
| screenshot-…-(<flow>).png | Artifact (image/png) |
| commands-(<flow>).json | Artifact (application/json) |
| ai-(<flow>).json | Artifact (application/json) |
| *.mp4 (video) tagged (<flow>) | Artifact (video/mp4) |
| maestro.log (debug) | Attached to every flow as a shared log |
Artifact path safety: the uploader refuses any file whose realpath escapes
the --test-output-dir / --debug-output roots, so a malicious flow can't
trick it into uploading /etc/passwd.
Recording video
maestro test doesn't record video — its LocalVideoRenderer classes are
only exercised by maestro record / maestro cloud. To get a video into
Devant Cloud, wrap the test run with the bundled devq-maestro-record helper,
which uses xcrun simctl io recordVideo (iOS) or adb shell screenrecord
(Android) under the hood:
devq-maestro-record \
--output build/maestro-results/recording.mp4 \
--device booted \
-- maestro test \
--format junit --output build/maestro-report.xml \
--test-output-dir build/maestro-results \
./flowsThen run the uploader as usual. Any mp4 / mov / webm at the top of
--test-output-dir whose name doesn't include a (<flow>) tag is treated
as a run-scoped recording and attached to every flow's last attempt — same
pattern as maestro.log.
devq-maestro-upload \
--junit build/maestro-report.xml \
--test-output-dir build/maestro-resultsdevq-maestro-record mirrors the child's exit code, so CI jobs that decide
pass/fail from maestro test keep working unchanged. Android tip:
screenrecord caps each invocation at 180s — split very long suites
across multiple flow runs.
Limitations
- No retries. Maestro's JUnit output reports a single execution per flow; we always emit one attempt. If you re-run a failing flow, run the uploader twice.
- No per-step trees. Maestro doesn't put step / command details in JUnit
XML; the
commands-*.jsonfile holds them, but parsing it into Devant Cloud steps would be a future enhancement. timeprecision. Maestro reports seconds with millisecond precision (time="421.573"); the uploader rounds to whole milliseconds for storage.- AI report files named by file stem (
ai-(01-launch-settings).json) rather than the flow'sname:field aren't yet matched per-flow — they'll be picked up by a future enhancement that consults the file path. Theai-(<flow.name>).jsonfiles Maestro 1.x emits do match.
