Skip to content

Releases: enzomanuelmangano/ennio

v0.0.9 — MCP server, Android backend, in-house HID

09 Jun 19:51
9431d05

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS is the primary target; Android is newer (opt-in via --android) —
its full example suite runs green and stable on CI, but it has seen less
real-world use. The 0.0.x line is a public preview, not a stability commitment.

The biggest release so far: ennio grows an Android backend, an MCP server so AI agents can drive a device through the same pipeline as test flows, an in-house HID layer that drops the last external dependency, and a rewritten CLI. Install is now global-first: npm install -g @reactiive/ennio.

MCP server

  • ennio mcp — serve ennio over the Model Context Protocol (stdio). An agent reads the screen (ennio_describe), decides, and acts (ennio_tap, ennio_swipe, ennio_input_text, …) through the same find → settle → actuate pipeline a test run uses; taps and swipes always go through the HID driver, never a passthrough (#59)
  • Versioned tool contract — structured { ok, data | error } envelopes on every tool, one selector model (testID | text | normalized [0,1] point), capability negotiation via ennio_status, side-effect-honest readOnly flags — all enforced by an executable conformance suite (#59)
  • Tool-agnostic by design: works identically with Claude Code, Cursor, Cline, or a hand-rolled client (#59)

Android backend (opt-in)

  • --android (or ENNIO_PLATFORM=android) — drive an emulator/device over adb with in-process agent injection: deterministic JVMTI attach on debuggable builds, ptrace remote-dlopen on non-debuggable release builds (#52)
  • Focus-identity tracking, regex text matchers, and a network-idle submit gate in the Java agent (#58)
  • Full 40-flow example suite green and stable on CI — three consecutive zero-retry runs; injection rides out emulator soft-lock windows with socket-bind verification and escalating relaunch backoff (#60)

In-house HID

  • idb_companion is gone. Touches are posted by enniohid, an in-house helper that links Xcode's own CoreSimulator / SimulatorKit and posts Indigo HID events directly — same pipeline as a physical finger, one persistent process per run, ~5 ms per event (#46)

New CLI

  • Rewritten command-line experience: animated live view while flows run, global-install-ready, update notice, and crash reports (#56)
  • Flags rationalized: app reuse is now default-on between flows (soft-reset instead of full relaunch — much faster suites); --disable-reuse-app opts out (#54)
  • Fast grey-box profile: --disable-animations + reuse-app + settle tightening (#51)

Reliability

  • iOS keyboard-swallow + autocorrect fixes — the Bluesky e2e suite runs 17/17 (#53)
  • TCC permission grant that sticks, plus a loud warning when cross-process AX is blind
  • Tab recovery asks the app for its tab bar instead of guessing from a name list

Docs

  • READMEs refreshed: global install (npm install -g @reactiive/ennio) instead of a devDependency, MCP section, full CLI command list, corrected injection wording (SIMCTL_CHILD_*, not simulator-wide env), current Android status (#55, #61)

Full Changelog: v0.0.8...v0.0.9

E2E fixtures v2

09 Jun 01:35
d2b3631

Choose a tag to compare

Example app APK (arm64-v8a + x86_64) with native Android action sheet (Alert) — enables g-action-sheet on Android. Supersedes v1 for the Android example e2e suite.

E2E fixture binaries

07 Jun 11:10

Choose a tag to compare

Prebuilt simulator app fixtures for CI e2e runs — the suites test the
runner, not the app, so these binaries are stable fixtures rather than
per-run builds.

asset what sha256
EnnioExample-app.zip EnnioExample.app, Release-iphonesimulator, built from 2dd79a6-era example source a35c43c6ae36f44037f123c38260f24c2a5c9b1eb43610a11f3fb19a8e9d3998

Workflows verify the pinned SHA-256 before installing. To refresh after
an example-app source change: build Release for the simulator,
ditto -c -k --keepParent EnnioExample.app EnnioExample-app.zip,
upload here, and update the pinned hash in .github/workflows/ci-example.yml.

v0.0.8 — injection hardening & crash diagnosis

06 Jun 08:48

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS only. Expect rough edges; do not rely on it for production-critical test
suites yet. The 0.0.x line is a public preview, not a stability commitment.

Hardening release driven by #44 (SIGSEGV under injection on RN 0.85 / iOS 26): the in-app swizzles are now provably safe to attach, crashes are diagnosed instead of masked, and there's a safe mode to fall back on.

Injection safety

  • Signature-checked RN observer swizzle — mount-method candidates are type-encoding-checked before attaching; non-forwardable C++ signatures (e.g. scheduleTransaction: taking a by-value std::shared_ptr on RN 0.85) are skipped with their encoding logged. Wrappers forward via void * — zero ARC traffic on non-object args (#48)
  • Fabric commit signals preferred — on New-Architecture apps the observer now attaches a live Fabric mount method instead of the dead interop Paper selector; Paper apps unchanged. Measurably faster settle on New-Arch apps (#50)

Crash diagnosis

  • When the app dies under injection, the CLI now reports what actually happened: exception type, faulting-thread frames, whether libennio.dylib was loaded, and the .ips crash-report path — instead of a bare ennio socket not connected (#47)

Safe mode & bisect flags

  • ennio test --safe-mode — run with all in-app hooks disabled; settle falls back to view-hash polling (#47)
  • Granular kill switches for bisecting conflicts: ENNIO_DISABLE_RN_OBSERVER, ENNIO_DISABLE_TESTID_INDEX, ENNIO_DISABLE_SETTLE (#47)

CLI

  • Verbose per-step output is now the default; --quiet / -q suppresses it, --verbose still accepted and wins over --quiet (#49)
  • SHA-256 dylib verification — the prebuilt dylib is verified against prebuilt/manifest.json before injection; mismatches refuse loudly. Local dev builds and ENNIO_DYLIB_PATH overrides skip the check (#50)

Docs

  • README rewritten to match the shipped architecture: direct dylib injection, real +load gates, architecture-agnostic RN support (the New-Architecture requirement was wrong), and the actual prerequisite — a dev/debug simulator build (#50)

Full Changelog: v0.0.7...v0.0.8

v0.0.7

02 Jun 17:49

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS only. Expect rough edges; do not rely on it for production-critical test
suites yet. The 0.0.x line is a public preview, not a stability commitment.

Ennio's biggest release yet: a ground-up v2 runtime, a hardened CLI, and a fully automated, provenance-signed release pipeline. Spans everything since 0.0.5 (includes 0.0.6).

Highlights — v2 architecture

A complete rewrite of how Ennio drives the simulator.

  • Prebuilt universal dylib DYLD_INSERT_LIBRARIES'd into the app at launch — one binary, every New-Architecture RN version (a884fec)
  • Unix-socket transport + idb gRPC HID backend — every tap/swipe goes through a typed channel (a884fec)
  • Typed-RPC execution engine with a settle engine + structured CLI logging (#37)
  • OOP foundationRunner / Connection / Session / Reporter separation (#40)

CLI

  • ennio --version flag (#41)
  • Hardened doctor — Node / Xcode / idb / dylib / simulator checks with actionable output (#41)
  • npm update notifier — warns when a newer version is on the registry (#41)
  • idb preflight — detects missing idb_companion / fb-idb and installs with consent (ENNIO_AUTO_INSTALL_IDB=1, never silent, never in CI) (#43)

Security & correctness

  • Per-UDID socket isolation; dropped ennio-expo-plugin (#38)
  • Lenient-warning + conditional retry on tiny controls + idb maxBuffer fix (#39)
  • Correctness + performance fixes (#36)

Release infrastructure

  • npm OIDC Trusted Publishing — no long-lived NPM_TOKEN; short-lived signed identity (#42)
  • Provenance generated automatically; published with --provenance (3d03f62)
  • Third-party actions SHA-pinned; protected release environment approval gate (#42)
  • Prereleases auto-routed to the @beta dist-tag, stable → latest (c1aa87a)
  • build-dylibs.yml is the canonical release workflow; symbol-surface allowlist diff guards the dylib (7f00577)

Install (as a dev dependency):

npm install --save-dev @reactiive/ennio
# or
bun add -d @reactiive/ennio

Full changelog: v0.0.5...v0.0.7

v0.0.5 — iOS 26 tap reliability

19 May 20:03

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS only. Expect rough edges; do not rely on it for production-critical test
suites yet. The 0.0.x line is a public preview, not a stability commitment.

Third preview. Headline: iOS 26 tap reliability. Three platform-level
races that previously dropped taps during sheet presents, keyboard-up
states, and RNS stack pushes are now closed end-to-end. Example suite
40/40. Habits sign-up-and-delete passes from cold launch to backend
deletion.

What's new

Tap reliability fixes

Issue Fix Where
50 ms held tap claimed by iOS 26 sheet's pan recogniser → tap routed to sheet drag instead of the inner UITextField hidTap durations ≤ 50 ms clamp to instant down/up writer.ts
Missed focus tap silently dropped subsequent HID keystrokes typeText routes through pasteFromClipboard when bound to a testID — landing is keyed by testID, not the focused responder writer.ts
RNS first-touch race: first tap on a freshly mounted screen swallowed by RNSScreenStackView assertVisible / assertNotVisible / inputText auto re-fire the last tap once when the expected post-tap state never arrives maestro-runner.ts
Point taps doubled the sheet's surface offset (tapAt was screen-relative but the runner added the surface origin again) revert surfaceOffset from tapAt — point coordinates stay screen-relative writer.ts
Daemon vs. CLI parity: HID gRPC sometimes rejected fractional coordinates the CLI rounded round float x / y to ints in the persistent HID daemon hid-daemon.py
findLabelMatch picked the outer aggregator Pressable whose AX label happened to contain "Email" (RN auto-aggregates child Text into a parent's accessibilityLabel) rank candidates by label specificityUITextField.placeholder exact > accessibilityLabel exact > CONTAINS — so a text tap on "Email" lands on the leaf RCTUITextField EnnioRuntimeHelper.mm
viewIsHittableAtCenter only walked from hit-test result up toward the target; missed the RN shape where hit-test surfaces the wrapper and the target is one hop down bidirectional walk (target ancestor AND descendant accepted) EnnioRuntimeHelper.mm
getViewWindowFrameByLabel was returning screen-converted coords; sheets live in the same window so the second convertRect:toWindow:nil was a regression drop the screen conversion — return window-relative coords EnnioRuntimeHelper.mm

Podspec

React-FabricComponents was split out of React-Fabric in RN 0.78+; it owns
the TextInput shadow-tree headers. The podspec now adds the public/private
search paths and an explicit dependency so
buildReactNativeFromSource: true builds resolve them.

Cross-version verified

  • Example suite, iOS 26: 40 / 40 PASS.
  • Habits app signup-delete.yaml, iOS 26: cold launch → sign up via email → today tab → settings tab → delete account → back to marketing screen.

Install

bun add @reactiive/ennio react-native-nitro-modules
bun add -d @reactiive/ennio-expo-plugin

Same setup as v0.0.4 — add the plugin to app.json, npx expo prebuild --clean,
npx expo run:ios, write a Maestro YAML.

Limitations

Unchanged from v0.0.4 — iOS only, Bridgeless / Fabric only, Metro must be
running.


Full Changelog: v0.0.4...v0.0.5

v0.0.4 — experimental preview

17 May 21:13

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS only. Expect rough edges; do not rely on it for production-critical test
suites yet. The 0.0.x line is a public preview, not a stability commitment.

Second preview release. Headline: native UIKit / SwiftUI component coverage.
Every common native iOS UI piece a React Native app uses is now driven from
Ennio end-to-end, including the iOS 26 SwiftUI-hosted variants where the
legacy UIKit class is gone from the view tree.

What's new

Native component handlers (Unix-domain control socket)

All routed through a new in-app socket so the handler isn't queued behind a
busy JS thread.

Component Mechanism Why
UITabBarController (bottom tabs) selectedIndex via UITabBarControllerDelegate (faster path) iOS 26 liquid-glass tab bar takes 2 s+ when routed through CDP — socket bypass makes it ~3 ms
UIAlertController walk window scenes + invoke UIAlertAction.handler alert lives on its own UIWindowLevelAlert window
Header items (headerLeft / headerRight) existing getViewWindowFrame + HID tap RNScreens custom views are reachable in the tree
UIRefreshControl pull-to-refresh two consecutive HID swipes single swipe is flaky on iOS 26 simulator
UIPickerView (@react-native-picker/picker) selectRow:inComponent: + delegate notify HID swipes against wheels are unreliable
UISearchBar (iOS 26 SwiftUI host) walk for UISearchBarTextField (the surviving private text-field class) iOS 26 replaced UISearchBar with InlineSearchBarViewRepresentation
UISegmentedControl setSelectedSegmentIndex: + UIControlEventValueChanged text-tap retry loop was ~14 s per tap
UIDatePicker spinner multi-component picker walk inner 3-wheel UIPickerView reachable via the same picker handler

Runner

  • tap() text-only fast-paths chained in order: tab → search-bar focus → segmented → picker → alert → HID
  • inputText / eraseText auto-route to native UISearchBar ops when a search field is focused
  • Per-flow profile summary in verbose mode (bucket / count / total / avg / pct)
  • Per-step Δ column in --verbose log so the slow step is obvious
  • HID daemon gains key, keyrep, and text ops — eraseText: 50 drops from ~8 s of subprocess spawns to ~50 ms in one gRPC call

Cross-version verified

iOS 26 (iPhone Air) full suite: 41 / 41 PASS.
iOS 18.5 (iPhone 16 Pro) native subset: 7 / 7 PASS.

Install

bun add @reactiive/ennio react-native-nitro-modules
bun add -d @reactiive/ennio-expo-plugin

Same setup as v0.0.3 — add the plugin to app.json, npx expo prebuild --clean,
npx expo run:ios, write a Maestro YAML.

Limitations

  • iOS only. Android scaffolding exists but no runtime.
  • Metro must be running. No Metro → no Hermes Inspector → CLI errors out.
  • Bridgeless / Fabric only. Old-architecture RN is not supported.
  • iOS 26 SwiftUI-hosted components. The search-bar handler walks
    UISearchBarTextField rather than UISearchBar; if Apple migrates more
    components similarly (segmented control, picker) the same workaround pattern
    applies.

Inventory not yet covered

  • UIMenu / UIContextMenuInteraction / zeego dropdown — out-of-process UI,
    needs private accessibility-audit APIs; deferred to a separate branch.
  • Native UIActionSheet (deprecated).
  • Apple Pay (PKPaymentAuthorizationViewController) — system sheet, can't be
    driven from the app process.

Full Changelog: v0.0.3...v0.0.4

v0.0.3 — Experimental preview

16 May 15:36

Choose a tag to compare

Warning

Experimental. Ennio is at an early experimental stage. APIs, package
names, internals, and behavior may change without notice between releases.
iOS only. Expect rough edges; do not rely on it for production-critical test
suites yet. The 0.0.x line is a public preview, not a stability commitment.

First tagged release of Ennio — a Maestro-compatible E2E test runner for
React Native iOS that drives the app over Hermes Inspector (CDP) and actuates
real CoreSimulator touches via a persistent idb HID daemon.

run-flow.mp4

Install

bun add @reactiive/ennio react-native-nitro-modules
bun add -d @reactiive/ennio-expo-plugin

Then add "@reactiive/ennio-expo-plugin" to your app.json plugins,
npx expo prebuild --clean, npx expo run:ios, and write a Maestro YAML.
Full walkthrough in the README.

Limitations

  • iOS only. Some Android scaffolding exists in the source tree but no
    runtime, no JNI hook, no JS-thread executor. Adding the plugin to an
    Android-only build is a no-op.
  • Metro must be running. With no Metro, there is no Hermes Inspector to
    connect to; the CLI errors out immediately.
  • Bridgeless / Fabric only. Old-architecture RN is not supported; the
    shadow-tree traverser and the React-commit hook assume the new arch.

Full Changelog: https://github.com/enzomanuelmangano/ennio/commits/v0.0.3