pub struct App { /* private fields */ }Implementations§
Source§impl App
impl App
Sourcepub fn new(driver: SimctlDriver, simctl: SimctlClient) -> Self
pub fn new(driver: SimctlDriver, simctl: SimctlClient) -> Self
Construct from a fully-wired driver + simctl client. Use this when you already manage Cell / UDID lifecycle externally.
Back-compat constructor: still accepts SimctlDriver (alias to
IosDriver) and SimctlClient; internally wraps into
Box<dyn Driver> and Box::new(IosDeviceControl::with_client(...)).
Sourcepub fn new_with(driver: Box<dyn Driver>, device: Box<dyn DeviceControl>) -> Self
pub fn new_with(driver: Box<dyn Driver>, device: Box<dyn DeviceControl>) -> Self
Generic constructor for cross-platform tests. Use this when
constructing with non-iOS Driver / DeviceControl impls.
Sourcepub fn http_runner_client(&self) -> Option<&HttpRunnerClient>
pub fn http_runner_client(&self) -> Option<&HttpRunnerClient>
Accessor for the underlying HTTP runner client, used by
Session to drive the /session/* routes. Returns None
when the driver is not backed by an HTTP runner (e.g. mock
driver in tests).
Sourcepub async fn open_session(
self,
bundle_id: &str,
activate: bool,
) -> Result<Session, ExpectationFailure>
pub async fn open_session( self, bundle_id: &str, activate: bool, ) -> Result<Session, ExpectationFailure>
Open a runner-side session bound to bundle_id.
Subsequent requests via the returned Session send the
Session-Id header and skip per-request activation entirely.
activate = true causes the runner to .activate() the target
once as part of the open (idiomatic when the target may not be
foregrounded yet). activate = false opens a passive binding
suitable when the caller has already ensured foreground state.
This is the recommended surface for long-running gates. See the
Session docs for the lifecycle contract.
Sourcepub async fn open_session_in_place(
&mut self,
bundle_id: &str,
activate: bool,
) -> Result<(), ExpectationFailure>
pub async fn open_session_in_place( &mut self, bundle_id: &str, activate: bool, ) -> Result<(), ExpectationFailure>
Open a runner session in place, keeping ownership of the App.
Same wire call + client mutation as Self::open_session, but
does not consume self into a Session. For long-lived shared
App holders (the MCP server, the real-sim harness) that drive
through &App and so cannot move into a Session. After this the
iOS session guard on every driving verb is satisfied (v2 break #1).
Sourcepub async fn connect_to_runner(port: u16) -> Result<Self, ExpectationFailure>
pub async fn connect_to_runner(port: u16) -> Result<Self, ExpectationFailure>
Convenience: connect to a runner on 127.0.0.1:{port} and probe
GET /health once. Returns App ready for sense+act calls.
Sourcepub fn connect_to_runner_lazy(port: u16) -> Self
pub fn connect_to_runner_lazy(port: u16) -> Self
Like Self::connect_to_runner but WITHOUT the startup health
probe: construction always succeeds, and a runner that is not up
yet is reported by the first real call instead (every request
path already tells the unreachable story with an actionable
hint, and the client memoizes the probe).
This exists for hosts whose own lifecycle starts before the
runner’s — the MCP server is launched by the MCP client at
client startup, and dying there left a permanently dead server
in every session where smix runner up came second.
Sourcepub async fn connect_to_runner_android(
port: u16,
) -> Result<Self, ExpectationFailure>
pub async fn connect_to_runner_android( port: u16, ) -> Result<Self, ExpectationFailure>
Connect to an Android Kotlin runner on 127.0.0.1:{port}
(the host-forwarded port that proxies to the device-side
runner instrumentation). Returns App ready for cross-platform
sense+act calls dispatched via AndroidDriver + AndroidDeviceControl.
Sourcepub fn with_udid<S: Into<String>>(self, udid: S) -> Self
pub fn with_udid<S: Into<String>>(self, udid: S) -> Self
Bind a UDID for lifecycle operations (launch/terminate/install/etc.).
Sourcepub fn with_bundle_id<S: Into<String>>(self, bundle: S) -> Self
pub fn with_bundle_id<S: Into<String>>(self, bundle: S) -> Self
Thread the target bundle id down to the driver, which forwards
it to the runner via the App-Bundle-Id HTTP header.
The two platforms use it for different things. iOS rebinds
XCUIApplication(bundleIdentifier:) per request, so calls stay
pinned to the right app even when something else briefly claims
foreground. Android has nothing to rebind — /tree walks every
attached window — and uses the package to spell the
<pkg>:id/<tag> resource ids that Compose emits on some
layouts.
Sourcepub fn with_auto_activate(self, activate: bool) -> Self
pub fn with_auto_activate(self, activate: bool) -> Self
Enable auto-activate on every request. The iOS runner
.activate()s the resolved target before operating. Costs one
XCUITest activate call per request (~50-100ms); opt-in.
iOS only. Android has no per-request activation to enable —
foregrounding there is a am start shell command, which is what
open_session(.., activate: true) and foreground() issue
once, deliberately, rather than on every request.
Sourcepub fn with_force_key_events(self, force: bool) -> Self
pub fn with_force_key_events(self, force: bool) -> Self
Force key-event dispatch mode on text-input verbs. Sends
Input-Dispatch-Mode: key-events header on every request.
Covers the RN hidden-input pattern where a11y-focus lookup
returns nothing. Also opt-in via smix run --force-key-events.
Sourcepub fn with_assert_screenshot_strict(self, strict: Option<bool>) -> Self
pub fn with_assert_screenshot_strict(self, strict: Option<bool>) -> Self
Inject the assert_screenshot strict-mode decision (v2 break #3).
Some(true) = missing baseline is a DriverError; Some(false) =
auto-record. None leaves the method on its
SMIX_ASSERT_SCREENSHOT_NO_AUTORECORD env fallback. smix run
always injects Some(resolved) from .smix/config.yaml.
Sourcepub fn with_launch_fresh_force_reinstall(self, force: Option<bool>) -> Self
pub fn with_launch_fresh_force_reinstall(self, force: Option<bool>) -> Self
Inject the launch_fresh force-reinstall decision (v2 break #3).
Some(true) = uninstall+install path; Some(false) = in-place
clear. None leaves the method on its
SMIX_LAUNCH_FRESH_FORCE_REINSTALL env fallback. smix run always
injects Some(resolved) from .smix/config.yaml.
pub fn udid(&self) -> Option<&str>
Sourcepub fn driver(&self) -> &dyn Driver
pub fn driver(&self) -> &dyn Driver
Direct access to underlying Driver trait object.
Use app.driver() for cross-platform calls; downcast to
IosDriver only if iOS-specific behavior needed.
Sourcepub fn simctl(&self) -> &SimctlClient
pub fn simctl(&self) -> &SimctlClient
Back-compat &SimctlClient accessor. iOS-only.
Panics on Android (use app.device() instead).
Sourcepub fn device(&self) -> &dyn DeviceControl
pub fn device(&self) -> &dyn DeviceControl
Cross-platform sim/host control trait object.
Sourcepub async fn clear_app_data(&self) -> Result<u64, ExpectationFailure>
pub async fn clear_app_data(&self) -> Result<u64, ExpectationFailure>
Clear the current session’s target app data
IN PLACE via cooperative XCUIApplication.terminate() + host
SimctlClient sandbox wipe + cooperative XCUIApplication.launch().
Preserves XCUITest binding and does NOT signal
com.apple.ReportCrash — the fix for the “simctl uninstall + install
and simctl terminate + simctl spawn rm still tripped.
Requires a Session-Id set on the driver (auto-populated by
smix run and by App::open_session); errors otherwise.
Requires an iOS driver + UDID (SimctlClient access for the
host-side wipe). Android is not supported yet — Android’s
UiAutomator lifecycle differs and needs its own charter.
Sourcepub async fn clear_app_data_with_launch(
&self,
launch_args: &[String],
launch_env: &BTreeMap<String, String>,
) -> Result<u64, ExpectationFailure>
pub async fn clear_app_data_with_launch( &self, launch_args: &[String], launch_env: &BTreeMap<String, String>, ) -> Result<u64, ExpectationFailure>
Same three-step orchestration as
Self::clear_app_data, but applies caller-supplied
launchArguments and launchEnvironment to the runner’s
cooperative launch step. Unblocks scaffolding like the Expo
SDK 57 dev-launcher server picker that clearAppData wipes
from persisted state and can no longer restore via a URL
Example yaml:
- clearAppData:
launchArgs: ["-EXInternalMetroPort", "8081"]
launchEnv:
EX_DEV_CLIENT_METRO_URL: "http://localhost:8081"Empty args + empty env is equivalent to
Self::clear_app_data.
pub async fn launch(&self, bundle_id: &str) -> Result<(), ExpectationFailure>
pub async fn terminate(&self, bundle_id: &str) -> Result<(), ExpectationFailure>
pub async fn install(&self, app_path: &str) -> Result<(), ExpectationFailure>
pub async fn uninstall(&self, bundle_id: &str) -> Result<(), ExpectationFailure>
Sourcepub async fn launch_fresh(
&self,
bundle_id: &str,
clear_state: bool,
clear_keychain: bool,
app_path: Option<&str>,
launch_arguments: &[String],
) -> Result<Vec<String>, ExpectationFailure>
pub async fn launch_fresh( &self, bundle_id: &str, clear_state: bool, clear_keychain: bool, app_path: Option<&str>, launch_arguments: &[String], ) -> Result<Vec<String>, ExpectationFailure>
Launch the app with optional state / keychain wipe before launch.
See plan_launch_fresh_calls for the op sequence semantics.
Returns the warnings produced by the planner (graceful fallback
path is taken when clear_state=true but app_path is None).
The caller should append these warnings to its own collector
(e.g. RunReport::warnings in the maestro adapter).
launch_arguments is process-level argv (simctl launch -- <args>). Empty &[] skips argv injection.
Sourcepub async fn set_permission(
&self,
bundle_id: &str,
permission: SimctlPermission,
action: PermissionAction,
) -> Result<(), ExpectationFailure>
pub async fn set_permission( &self, bundle_id: &str, permission: SimctlPermission, action: PermissionAction, ) -> Result<(), ExpectationFailure>
Apply a permission action to a bundle.
Maps maestro yaml permissions: { camera: allow|deny|unset } to simctl
privacy. Sense+act live in core; the adapter only translates
maestro yaml strings to the PermissionAction enum.
Sourcepub async fn launch_app_with_options(
&self,
opts: &LaunchAppOptions,
) -> Result<Vec<String>, ExpectationFailure>
pub async fn launch_app_with_options( &self, opts: &LaunchAppOptions, ) -> Result<Vec<String>, ExpectationFailure>
Typed launch entry: apply permissions in declaration order, then
dispatch to Self::launch_fresh (when clear_state /
clear_keychain) or simctl terminate + launch_with_args
(otherwise). Maps maestro yaml launchApp: { ... } in full
(permissions / arguments / clearState / clearKeychain). Returns
warnings emitted by launch_fresh (caller appends to its own
collector).
Sourcepub async fn set_animations_quiet(
&self,
quiet: bool,
) -> Result<(), ExpectationFailure>
pub async fn set_animations_quiet( &self, quiet: bool, ) -> Result<(), ExpectationFailure>
Ask the device to stop animating, and refuse if it did not.
The strength differs by platform and the wording does not paper
over it: Android zeroes three animation scales, which is off;
iOS gets Reduce Motion, which is weaker but is as far as smix
can push without the app under test cooperating. See
DeviceControl::set_animations_quiet.
Sourcepub async fn clear_user_defaults(
&self,
bundle_id: &str,
keys: &[String],
) -> Result<(), ExpectationFailure>
pub async fn clear_user_defaults( &self, bundle_id: &str, keys: &[String], ) -> Result<(), ExpectationFailure>
Delete keys from the target app’s persisted
user-defaults store (iOS: NSUserDefaults via simctl spawn defaults delete; Android: unsupported, explicit error).
Contract is “ensure keys absent” — already-absent keys are
success. Terminate the app first: a running process caches its
defaults in-memory and may rewrite keys at exit.
Motivating case: expo-dev-launcher persists the most recent deep link and re-delivers it after every JS bundle load; deleting its storage key between terminate and relaunch neutralizes the replay at the source.
pub async fn open_url(&self, url: &str) -> Result<(), ExpectationFailure>
Sourcepub async fn send_push(
&self,
bundle_id: &str,
apns_json_path: &str,
) -> Result<(), ExpectationFailure>
pub async fn send_push( &self, bundle_id: &str, apns_json_path: &str, ) -> Result<(), ExpectationFailure>
Deliver an APNS payload to bundle_id via simctl push.
The payload file must contain a JSON dictionary with at least an
aps key (per Apple’s spec). Mirrors maestro yaml sendPush:
once that command lands upstream — there is no public maestro
yaml sendPush today, so this is SDK-only surface.
pub async fn screenshot(&self) -> Result<Vec<u8>, ExpectationFailure>
Sourcepub async fn capture_bgra(&self) -> Result<CapturedFrame, ExpectationFailure>
pub async fn capture_bgra(&self) -> Result<CapturedFrame, ExpectationFailure>
Capture a frame preferring the fast raw-BGRA path (skips PNG
encode+decode for diff-loop consumers). Falls back to a PNG frame when
the direct IOSurface path is unavailable. See
DeviceControl::capture_bgra.
Since smix 2.0.0.
Sourcepub fn mark_fixture_action(&self, action_id: &str)
pub fn mark_fixture_action(&self, action_id: &str)
Register a fixture-side action anchor in the SDK ledger so that
the capsule_reconcile window can attribute the
kAXFirstResponderChangedNotification (1018) the fixture’s
UIKit modal present is about to emit. Use this immediately
before triggering a fixture-owned present path
(UIActivityViewController, UIDocumentPickerViewController,
SpringBoard system popup) that smix-driver would otherwise
leave unattributed — without it, the phantom focus change
inflates unattributed_count.
pub async fn tree(&self) -> Result<A11yNode, ExpectationFailure>
pub async fn describe(&self) -> Result<ScreenDescription, ExpectationFailure>
pub async fn find_one( &self, selector: &Selector, ) -> Result<Option<A11yNode>, ExpectationFailure>
pub async fn find_all( &self, selector: &Selector, ) -> Result<Vec<A11yNode>, ExpectationFailure>
pub async fn find( &self, selector: &Selector, ) -> Result<bool, ExpectationFailure>
pub async fn system_popups( &self, ) -> Result<Vec<SystemPopup>, ExpectationFailure>
Sourcepub async fn system_popup_action(
&self,
popup_id: &str,
button_id: &str,
) -> Result<bool, ExpectationFailure>
pub async fn system_popup_action( &self, popup_id: &str, button_id: &str, ) -> Result<bool, ExpectationFailure>
Tap a button on a previously enumerated system popup. popup_id
and button_id round-trip from system_popups() — the runner
walks the same scan order so callers don’t need to manage an id
map. Returns Ok(true) when matched and tapped, Ok(false) when
the runner returned 404 not_found (popup or button id stale).
Paired with system_popups() to close the sense/act loop on
iOS system popups.
Sourcepub async fn tap(
&self,
selector: &Selector,
) -> Result<ActOutcome, ExpectationFailure>
pub async fn tap( &self, selector: &Selector, ) -> Result<ActOutcome, ExpectationFailure>
Tap a selector, and report what the touch landed on.
Returns an outcome rather than unit. “A touch was synthesised at that coordinate” and “the element was tapped” are different claims, and this made the first while callers read the second — a consumer watched it succeed ten times against a button whose counter never moved.
The outcome carries what was aimed at, every named element
containing the point afterwards, and a verdict. The elements
come back even when the verdict is Confirmed, because the
verdict cannot see occlusion and the list can.
Sourcepub async fn tap_burst(
&self,
selector: &Selector,
times: u32,
interval_ms: Option<u32>,
hold_ms: Option<u32>,
) -> Result<(), ExpectationFailure>
pub async fn tap_burst( &self, selector: &Selector, times: u32, interval_ms: Option<u32>, hold_ms: Option<u32>, ) -> Result<(), ExpectationFailure>
Tap a selector times times in a row.
One resolve and one synthesise, with the touches spaced by
interval_ms on the event timeline. repeat around tapOn
sends each tap as its own request, which at ~400 ms per
synthesise makes a rapid-tap gesture undriveable — and leaves
the interval as whatever the round trip cost, so a flow cannot
tell a slow harness from a broken app.
None for either timing takes the runner’s default.
Sourcepub async fn tap_with_mode(
&self,
selector: &Selector,
mode: TapMode,
) -> Result<(), ExpectationFailure>
pub async fn tap_with_mode( &self, selector: &Selector, mode: TapMode, ) -> Result<(), ExpectationFailure>
Tap a selector via an explicit dispatch mode. Use
TapMode::DaemonProxySynthesize for RN Pressable buttons that
don’t fire onPress with the default tap() Apple-native-event
-chain dispatch. All other selectors should use tap(selector)
(no mode) — the default host-resolve plus tap_at_norm_coord
path is faster and works for non-RN-Pressable elements.
pub async fn fill( &self, selector: &Selector, text: &str, ) -> Result<(), ExpectationFailure>
pub async fn clear(&self, selector: &Selector) -> Result<(), ExpectationFailure>
pub async fn press_key(&self, key: KeyName) -> Result<(), ExpectationFailure>
pub async fn scroll( &self, selector: &Selector, direction: SwipeDirection, ) -> Result<(), ExpectationFailure>
pub async fn swipe_once( &self, direction: SwipeDirection, ) -> Result<(), ExpectationFailure>
pub async fn hide_keyboard(&self) -> Result<(), ExpectationFailure>
pub async fn go_back(&self) -> Result<(), ExpectationFailure>
Sourcepub async fn tap_at_coord(
&self,
nx: f64,
ny: f64,
) -> Result<(), ExpectationFailure>
pub async fn tap_at_coord( &self, nx: f64, ny: f64, ) -> Result<(), ExpectationFailure>
Tap at normalized (nx, ny) coordinates — escape hatch for coord-based maestro yaml port and other no-a11y-semantic scenarios. (nx, ny) MUST be in [0, 1] (normalized to viewport).
Escape hatch: the Selector surface still forbids xpath/coord
— this method is NOT a Selector, it is the direct
Apple-native-event-chain wire entry. Only tap is exposed;
fill_at_coord / anchor_at_coord are intentionally NOT
provided.
Prefer tap(&selector) for any path with a11y semantic. Use this
only for yaml-port edge cases (e.g. maestro point: "X%,Y%").
Sourcepub async fn tap_xcui(&self, id: &str) -> Result<(), ExpectationFailure>
pub async fn tap_xcui(&self, id: &str) -> Result<(), ExpectationFailure>
Tap via XCUIElement.tap() over the XCTest gesture-recognizer
chain instead of the default host-HID-at-coord path. The id
selector is resolved runner-side via
XCUIApplication.descendants(matching: .any) .matching(identifier:).firstMatch.tap().
Why this exists: SwiftUI .sheet / .alert /
.confirmationDialog / .fullScreenCover dismiss buttons
present in a separate modal window scene. The default
tap(&selector) resolves the button frame and injects an IOKit
event at that coord, but iOS routes the touch to the underlying
scene’s hit-target, so SwiftUI’s onTap closure for the
modal-window button never fires. XCUIElement.tap() operates on
the resolved element handle and reaches the binding regardless
of window topology.
Use tap(&selector) for everything else — the default path is faster
and works on non-modal SwiftUI / UIKit hierarchies.
Sourcepub async fn find_norm_coord(
&self,
selector: &Selector,
) -> Result<Option<(f64, f64)>, ExpectationFailure>
pub async fn find_norm_coord( &self, selector: &Selector, ) -> Result<Option<(f64, f64)>, ExpectationFailure>
Apple Vision OCR find. Returns the matching text observation’s
bounding box (UIKit normalized) or None. locales are BCP-47
language subtags; empty defaults to the SDK’s current locale
(["en"] if unset). Covers “lib without testID but with
visible text” scenarios.
Find a selector’s centroid as viewport-normalized (nx, ny).
Used by adapter AnchorRelative dispatch. Returns None when
the selector resolves no node / empty frame.
Sourcepub async fn webview_eval(&self, js: &str) -> Result<Value, ExpectationFailure>
pub async fn webview_eval(&self, js: &str) -> Result<Value, ExpectationFailure>
Eval JS against the app-side WKWebView bridge. Returns the JS result as a JSON Value. Bridge must be running in the target app.
pub async fn find_by_text_ocr( &self, text: &str, locales: &[String], ) -> Result<Option<OcrFrame>, ExpectationFailure>
Sourcepub async fn tap_by_text_ocr(
&self,
text: &str,
locales: &[String],
) -> Result<(), ExpectationFailure>
pub async fn tap_by_text_ocr( &self, text: &str, locales: &[String], ) -> Result<(), ExpectationFailure>
Find by OCR + tap at frame center via IOHID synthesize.
Convenience for the common OCR fallback path (OCR keyword → tap).
Returns ElementNotFound when OCR finds no match.
Sourcepub async fn swipe_at_coord(
&self,
from: (f64, f64),
to: (f64, f64),
) -> Result<(), ExpectationFailure>
pub async fn swipe_at_coord( &self, from: (f64, f64), to: (f64, f64), ) -> Result<(), ExpectationFailure>
Swipe between two normalized coordinate points — escape hatch for
coord-based maestro yaml port (swipe: { from: "X%,Y%", to: "X%,Y%" }).
Both points MUST be in [0, 1].
Escape hatch: companion to Self::tap_at_coord. The
Selector surface still forbids xpath/coord — this method is NOT
a Selector, it is the direct Apple-native-event-chain wire
entry. Only the tap and swipe coord forms are exposed;
fill_at_coord / anchor_at_coord / hover_at_coord are
intentionally NOT provided.
Sourcepub async fn swipe_from(
&self,
direction: SwipeDirection,
from: &Selector,
) -> Result<(), ExpectationFailure>
pub async fn swipe_from( &self, direction: SwipeDirection, from: &Selector, ) -> Result<(), ExpectationFailure>
Swipe one gesture anchored at an element: the gesture starts from the selector’s resolved centroid instead of the viewport centre.
Composite of Self::find_norm_coord + Self::swipe_at_coord.
Self::swipe_once and Self::scroll_screen always start at
the viewport centre, so a caller holding an anchor element had no
way to express “drag this row” — that gap is what this closes.
direction follows the same navigation convention as
Self::swipe_once / Self::scroll_screen: Down advances the
content downward, so the finger travels upward.
Sourcepub async fn scroll_screen(
&self,
direction: SwipeDirection,
) -> Result<(), ExpectationFailure>
pub async fn scroll_screen( &self, direction: SwipeDirection, ) -> Result<(), ExpectationFailure>
Viewport scroll one swipe in the given direction — no selector required.
Maps to maestro yaml scroll: (bare, no args, defaults to down).
Implementation: a single normalized-coord swipe from the viewport
center to one edge (sense+act layer). Self::scroll is the
scroll-until-visible composite and is orthogonal; scroll_screen
is a pure act primitive.
Sourcepub async fn wait_for_not_visible(
&self,
selector: &Selector,
timeout: Duration,
) -> Result<(), ExpectationFailure>
pub async fn wait_for_not_visible( &self, selector: &Selector, timeout: Duration, ) -> Result<(), ExpectationFailure>
Assert that the selector is NOT visible. Dual of
Self::assert_visible. Maps to maestro yaml assertNotVisible:.
Assertion is a core sense+assertion primitive, not an
adapter-only synthesis. Uses a single non-waiting
Self::find probe; if the selector matches, raise
AssertionFailed.
Wait until the selector is NOT visible. Dual of Self::wait_for.
Polls Self::find at 250ms intervals; returns Ok the first instant
the element is absent. Returns AssertionFailed if the element is
still visible after timeout elapses.
The assertion+sense composite is a core platform capability,
not adapter-only synthesis. Maps to maestro yaml
extendedWaitUntil: { notVisible: ... , timeout: N }.
Sourcepub async fn assert_screenshot(
&self,
baseline_path: &Path,
max_hamming: u32,
) -> Result<AssertScreenshotOutcome, ExpectationFailure>
pub async fn assert_screenshot( &self, baseline_path: &Path, max_hamming: u32, ) -> Result<AssertScreenshotOutcome, ExpectationFailure>
Assert the current sim screenshot matches a recorded baseline
PNG via 64-bit dhash perceptual diff. Maestro
assertScreenshot: <baseline-path>.
Baseline lifecycle (same as maestro):
- Baseline missing → write the captured PNG + return
Recorded { path }(auto-record default). SMIX_ASSERT_SCREENSHOT_NO_AUTORECORD=1env → strict mode: missing baseline =DriverError.- Baseline present → dhash(baseline) vs dhash(current) → hamming
distance;
≤ max_hamming=Matched { hamming }, otherwiseAssertionFailed.
max_hamming typically ≤ 10 (adapter runtime arm pins 5).
pub async fn assert_not_visible( &self, selector: &Selector, ) -> Result<(), ExpectationFailure>
Sourcepub async fn set_clipboard(&self, text: &str) -> Result<(), ExpectationFailure>
pub async fn set_clipboard(&self, text: &str) -> Result<(), ExpectationFailure>
Write text to the iOS Simulator device pasteboard via
xcrun simctl pbcopy <udid>. Maps to maestro yaml
setClipboard: "literal".
Clipboard set is a core act primitive. Uses the simctl host-side
path (device-scoped, explicit UDID) rather than the swift sim-side
UIPasteboard wire — SimctlClient::pasteboard_set already has
a stable wire, no need to add a new swift route.
Sourcepub async fn get_clipboard(&self) -> Result<String, ExpectationFailure>
pub async fn get_clipboard(&self) -> Result<String, ExpectationFailure>
Read the current iOS Simulator device pasteboard via
xcrun simctl pbpaste <udid>. Returns the raw string (may be empty).
Sourcepub async fn paste_text(
&self,
text: Option<&str>,
) -> Result<(), ExpectationFailure>
pub async fn paste_text( &self, text: Option<&str>, ) -> Result<(), ExpectationFailure>
Paste text into the currently-focused input field.
Maps maestro yaml pasteText: "literal" (text-bearing form) and
bare - pasteText (None form, reads current clipboard first).
Both forms preserve the clipboard side-effect maestro yaml users implicitly rely on (literal form writes clipboard so the post-flow pasteboard mirrors the typed text — same as native “paste from clipboard” UX).
Sourcepub async fn double_tap(
&self,
selector: &Selector,
) -> Result<(), ExpectationFailure>
pub async fn double_tap( &self, selector: &Selector, ) -> Result<(), ExpectationFailure>
Double-tap an element. Maps to maestro yaml
doubleTapOn: <selector>. Backed by XCUIElement.doubleTap() on
the swift sim side.
Sourcepub async fn long_press(
&self,
selector: &Selector,
duration: Duration,
) -> Result<(), ExpectationFailure>
pub async fn long_press( &self, selector: &Selector, duration: Duration, ) -> Result<(), ExpectationFailure>
Long-press an element for duration. Maps to maestro yaml
longPressOn (optional duration: ms, default 500 on the
adapter side). Backed by XCUIElement.press(forDuration:) on
the swift sim side.
Sourcepub async fn long_press_capturing(
&self,
selector: &Selector,
duration: Duration,
) -> Result<PressCapture, ExpectationFailure>
pub async fn long_press_capturing( &self, selector: &Selector, duration: Duration, ) -> Result<PressCapture, ExpectationFailure>
Long-press an element while capturing frames of the held state.
The two halves run on different transports on purpose. The press
goes to the runner, and every runner route that touches
XCUITest queues behind an in-flight gesture — a /tree sent one
second into a four-second press was measured returning only
after the press ended. So the frames come from simctl, which
does not go through XCUITest and answers while the runner is
occupied.
Each frame is judged against the interval the touch was provably down for, and frames that cannot be placed inside it say so. Handing back an unjudged frame is what this exists to stop: a consumer who screenshotted alongside a press got the resting screen and read it as “the animation never fired”.
Sourcepub async fn set_location(
&self,
latitude: f64,
longitude: f64,
) -> Result<(), ExpectationFailure>
pub async fn set_location( &self, latitude: f64, longitude: f64, ) -> Result<(), ExpectationFailure>
Set sim location. Maestro setLocation: { latitude, longitude }.
Sourcepub async fn travel(
&self,
points: &[(f64, f64)],
speed_mps: Option<f64>,
) -> Result<(), ExpectationFailure>
pub async fn travel( &self, points: &[(f64, f64)], speed_mps: Option<f64>, ) -> Result<(), ExpectationFailure>
Interpolate sim location along waypoints. Maestro travel.
Fire-and-return: simctl injects scenario and returns immediately;
sim continues interpolation in background. Caller must explicitly
waitForAnimationToEnd / sleep if downstream logic depends on
playback completion.
Sourcepub async fn add_media(
&self,
paths: &[String],
) -> Result<(), ExpectationFailure>
pub async fn add_media( &self, paths: &[String], ) -> Result<(), ExpectationFailure>
Add photos / videos / contacts to the sim library. Maestro
addMedia: <path> (scalar) or addMedia: [paths] (array;
adapter flattens to Vec).
Sourcepub async fn start_recording(
&self,
path: &str,
) -> Result<(), ExpectationFailure>
pub async fn start_recording( &self, path: &str, ) -> Result<(), ExpectationFailure>
Start recording the sim display to path. Maestro
startRecording: <path>. Spawns xcrun simctl io recordVideo as
a long-running child; returns immediately. Errors if a recording
is already in progress (call stop_recording first — no silent
no-op).
Sourcepub async fn stop_recording(&self) -> Result<(), ExpectationFailure>
pub async fn stop_recording(&self) -> Result<(), ExpectationFailure>
Stop the active recording (SIGINT-and-wait simctl child; flushes
mp4 trailer). Maestro stopRecording. Errors if no recording is
active — explicit DriverError + hint, not a silent no-op.
Sourcepub async fn set_orientation(
&self,
orientation: MaestroOrientation,
) -> Result<(), ExpectationFailure>
pub async fn set_orientation( &self, orientation: MaestroOrientation, ) -> Result<(), ExpectationFailure>
Rotate sim. Maestro setOrientation: portrait | portraitUpsideDown | landscapeLeft | landscapeRight.
Walks driver.set_orientation → POST /set-orientation → swift
XCUIDevice.shared.orientation.
Sourcepub async fn set_permissions(
&self,
bundle_id: &str,
permissions: &[(SimctlPermission, PermissionAction)],
) -> Result<(), ExpectationFailure>
pub async fn set_permissions( &self, bundle_id: &str, permissions: &[(SimctlPermission, PermissionAction)], ) -> Result<(), ExpectationFailure>
Batch permission setter. Maestro setPermissions: { camera: allow, location: deny, ... } (top-level command, distinct from
launchApp.permissions). Reuses set_permission per entry
(sequential apply; fail-fast on first error).
Sourcepub async fn copy_text_from(
&self,
selector: &Selector,
) -> Result<(), ExpectationFailure>
pub async fn copy_text_from( &self, selector: &Selector, ) -> Result<(), ExpectationFailure>
Read text content from the matched element and write it to the
device pasteboard. Maps to maestro yaml copyTextFrom: <selector>. Field priority follows the maestro iOS driver:
value → text → label. All three empty raises
AssertionFailed — no silent no-op.
Sourcepub async fn start_capsule_recording(&self) -> Result<(), ExpectationFailure>
pub async fn start_capsule_recording(&self) -> Result<(), ExpectationFailure>
Start Capsule recording — triggers the UITest runner
EventRecorder.installSwizzle registration path (if
TEST_RUNNER_SMIX_RECORD_ENABLED=1 is set in the env) and
clears the SDK-internal issued-action ledger. The runner
must be started with TEST_RUNNER_SMIX_RECORD_ENABLED=1;
otherwise swift-side recordEnabled is false, installSwizzle
is skipped, and /record/start returns 404.
Sourcepub async fn stop_capsule_recording_and_reconcile(
&self,
window_ms: Option<u64>,
) -> Result<CapsuleReconciliation, ExpectationFailure>
pub async fn stop_capsule_recording_and_reconcile( &self, window_ms: Option<u64>, ) -> Result<CapsuleReconciliation, ExpectationFailure>
Stop recording and reconcile. window_ms = None uses
DEFAULT_RECONCILE_WINDOW_MS (500 ms). Returned
CapsuleReconciliation includes the full
unattributed_events detail.
pub async fn foreground( &self, bundle_id: &str, ) -> Result<(), ExpectationFailure>
pub async fn wait_for( &self, selector: &Selector, timeout: Duration, ) -> Result<A11yNode, ExpectationFailure>
Sourcepub async fn assert_visible(
&self,
selector: &Selector,
) -> Result<(), ExpectationFailure>
pub async fn assert_visible( &self, selector: &Selector, ) -> Result<(), ExpectationFailure>
Assert that the selector matches a visible element. Re-uses
wait_for semantics (5s default budget) with NotVisible
failure code.
Sourcepub async fn assert_enabled(
&self,
selector: &Selector,
) -> Result<(), ExpectationFailure>
pub async fn assert_enabled( &self, selector: &Selector, ) -> Result<(), ExpectationFailure>
Assert that the matched element has enabled = true.
Sourcepub async fn assert_text(&self, literal: &str) -> Result<(), ExpectationFailure>
pub async fn assert_text(&self, literal: &str) -> Result<(), ExpectationFailure>
Assert that the screen contains at least one element whose text / label / 6-field OR scan matches the literal. Useful for “page rendered” smoke checks without crafting a full selector tree.