Add XCUITest flow interpreter for iOS - #5
Merged
Merged
Conversation
The skill becomes a core (rules that bind in every phase, a step map, steps 1-3) plus one reference per phase: build.md (step 0a-0c), drive.md (step 0d-0f and the drive rules), evidence.md and funding.md. Rule ids are unchanged. The skill-read gate delivers the build slice with the build scripts and the drive and evidence slices with capture-buy-quote.sh and with every maestro drive. The funding slice arrives with a value-moving log-attempt.sh call. build-and-test-slices.test.py holds the core and each slice under 20,000 characters and checks that every pre-split rule id is placed once.
j0ntz
force-pushed
the
jon/xcuitest-interpreter
branch
from
October 1, 2026 21:38
a085b79 to
7ea7001
Compare
One generic XCUITest bundle (xcuitest/EdgeFlowRunner) interprets
Maestro flow YAML at run time. The host converts the YAML to JSON
(maestro-yaml-to-json.rb) and passes it through xcodebuild
test-without-building via TEST_RUNNER_ env, so the bundle is built
once per Xcode build, runtime and source hash (xcuitest-build.sh,
cached under ~/Library/Caches/edge-flow-runner, mkdir lock).
xcuitest-run.sh drives one flow on one UDID with per-run JSON, log
and result bundle under TMPDIR keyed by UDID, and no host port. It
stops the slot's maestro MCP daemon by explicit PID first.
The runner preflights every command before step 1 and fails naming
the flow and step for anything unsupported. ${} and evalScript run
in JavaScriptCore. A swizzle caps XCUITest quiescence waits (default
1s, --quiescence-cap or env EDGE_QUIESCENCE_CAP per flow, 0 skips)
and logs each cap hit with its step.
build-and-test's new ios-flows-run-on-xcuitest rule makes xcuitest-run.sh the default iOS flow runner; the maestro CLI runs iOS flows only when a task asks or the interpreter's preflight rejects a command. Android stays on Maestro. capture-buy-quote.sh takes --driver xcuitest|maestro (default xcuitest). references/xcuitest-interpreter.md lists the supported commands, the Maestro semantics kept, the quiescence cap, the test-mode animation switch and the unsupported commands.
The search field holds the typed text, so a plain name match hit the
field instead of the first row; pick match 1 of a contains-regex.
Header env uses ${KEY || default} so runFlow env wins, and the
Exchange tap retries through the post-login hidden tab bar.
YOLO auto-login can finish mid-entry and leave the PIN screen, so a later tap found no digit and failed the flow. Each tap now runs only while Exit PIN is visible.
The Search Wallets text survives navigation, so inputText appended to a stale query and the row tap hit the search field.
A preflight survey of the agent task flows under /tmp found flows the interpreter rejected, almost all on tapOn point: and hideKeyboard. Add those and the remaining parity commands so every surveyed flow runs natively: - tapOn point: as screen percentages or absolute points, and relative to the element when a selector is present - longPressOn (3s hold, as Maestro on iOS) - hideKeyboard with Maestro's iOS behavior: nothing when no keyboard is up, else a short swipe up from the screen center, then a short swipe left - copyTextFrom and pasteText, with maestro.copiedText in the script context - inputRandomText - enabled: on selectors - scrollUntilVisible centerElement: - back and pressKey: back as no-ops, as on Maestro iOS launchApp clearState: true stays rejected.
j0ntz
force-pushed
the
jon/xcuitest-interpreter
branch
from
October 1, 2026 23:00
7ea7001 to
1d19ad8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Asana task
Adds a native XCUITest flow interpreter and makes it the default iOS driver for build-and-test. Android stays on Maestro.
Runner (
build-and-test/xcuitest/EdgeFlowRunner,scripts/xcuitest-build.sh,scripts/xcuitest-run.sh):maestro-yaml-to-json.rbconverts the flow (inliningrunFlowandretryfiles) and the JSON reaches the runner through aTEST_RUNNER_env var. Each run is onexcodebuild test-without-building.~/Library/Caches/edge-flow-runner. Amkdirlock lets concurrent slots share one build.TMPDIRkeyed by UDID. One xcodebuild per UDID, no host port. The slot's maestro MCP daemon is stopped by PID, never by a broad pkill.idmatches testID,textmatches label, value or placeholder, both as whole-string case-insensitive regexes.indexorders on-screen matches.${...}andevalScriptrun in JavaScriptCore, so expressions are real JavaScript.waitForAnimationToEndwaits for two consecutive identical screenshots.XCUIApplicationProcesscaps XCUITest's quiescence waits: default 1s,--quiescence-cap Nper run, envEDGE_QUIESCENCE_CAPper flow, 0 skips the wait. Each hit prints aquiescence cap hitline naming the step, and the last line counts them.--animations off|fast|on(defaultoff) passes-EdgeTestAnimationsonlaunchAppfor the edge-react-gui test-mode switch.Routing: the build-and-test rule
ios-flows-run-on-xcuitestmakesxcuitest-run.shthe default iOS runner. Maestro runs iOS only when a task asks for it or preflight rejects a command.capture-buy-quote.shgains--driver xcuitest|maestro(defaultxcuitest).references/xcuitest-interpreter.mddocuments the runner.Library flow fixes, found while driving all 12 flows natively. Each one is also a latent Maestro race:
walletListRow.<name>.<code>.Orch flow coverage (second commit). The target is every flow an orch run drives. A preflight survey of the agent task flows under
/tmprejected about half of them ontapOn point:andhideKeyboard. This commit adds:tapOn point:in both Maestro forms, percentages ("50%,80%") and absolute points ("120,640"), through a normalizedXCUICoordinateon the app.hideKeyboard, following Maestro 2.6.1 on iOS: nothing when no keyboard is up, otherwise a short swipe up from the screen center, then a short swipe left if the keyboard is still there.longPressOn(3s, selector orpoint:),assertVisiblewithenabled:,copyTextFromandpasteText(the copied text is alsomaestro.copiedTextin JavaScriptCore),inputRandomText, andscrollUntilVisiblewithcenterElement:.backandpressKey: backare accepted and do nothing, as on Maestro iOS.centerElement: an element that is on screen but outside the center band is dragged by its own distance from the screen center. Maestro repeats its full swipe, which carried a wallet row past the band and off screen.launchApp clearState: truestays rejected on purpose because it wipes the roster accounts. Porting the edge-react-guimaestro/suite is not a goal.Dependencies
EdgeApp/edge-react-gui#6232 adds the
EdgeTestAnimationsswitch. The runner works without it;--animationsthen has no effect.Testing
iOS simulator, edge-react-gui debug build at 2efa138b0 plus the gui PR. Default cap 1s, animations off.
All 12 library flows pass with 0 quiescence cap hits:
The last five ran inside a swap drive, a send drive and a throwaway create/delete drive. The swap and the send were real mainnet transactions.
--quiescence-cap 0.05hit it once with the step logged.waitForAnimationToEndsettles in 0.16s withoffand runs to its 5s timeout withon. login-if-needed took 7.86s on / 7.82s off and open-settings 2.01s on / 2.09s off, all passing with 0 cap hits. Spinners never hold the quiescence wait: taps beside one take 1.5s in either mode.verify-repopasses.Orch flow coverage commit, iOS simulator, edge-react-gui debug build at b50957606:
FlowPreflightcompiled into a CLI and run over every agent task flow under/tmp: 302 flows, 302 pass, 0 rejected. A flow with an unknown command is still rejected.tapOn point:percent and absoluteinputRandomText,copyTextFrom,pasteText,hideKeyboardscrollUntilVisible centerElement:,longPressOnassertVisible enabled:true and false,centerElement:enabled: falseparsed as true, the scroll drag ended in a fling, a snapshot race in element lookup, and the centering overshoot described above.Deep-link commits, iOS simulator, same debug build. The branch is rebased onto the build-and-test split, so each rule edit now sits in the slice that owns it (
references/drive.md,references/build.md), andhooks/tests/build-and-test-slices.test.pypasses on the branch tree.openLinkhands the URL to the app under test throughXCUIApplication.open. Bothedge://links andhttps://edge.app/redirect/payment/links arrive with no "Open in Edge?" dialog. Scalar andlink:forms are accepted, andautoVerifyandbrowserare accepted and ignored.select-swap-pair(SRC_ASSET,DST_ASSET),buy-quote-input(BUY_ASSET) andsend-to-address(CURRENCY_CODE) each takeMANUAL_PATH: "true"to walk the old path. Existing params keep their meaning.send-to-addresswith noCURRENCY_CODEtakes the manual path, since the payment link needs a currency code.send-to-addressanswers it withWALLET_SEARCH.MANUAL_PATH, ETH to LTCMANUAL_PATHhideKeyboardon the Buy amount fieldNo send or swap was confirmed in these drives. The send flow was run without its final slider step.
The screenshots below come from the interpreter's
takeScreenshotduring these runs.Test evidence
7ea7001Document deep-link drives per driver
2026-10-01
1. find wallet
4. ramp region
9. throwaway created
12. tap point
13. copy paste random text
14. hide keyboard
15. long press menu
16. scroll center element
17. assert enabled
21. reg throwaway deleted
22. openlink swap pair
23. openlink send prefilled
24. openlink swap quote
25. openlink buy amount