home / skills / mikekelly / outside-in-testing-ios-apps / outside-in-testing-ios-apps
This skill guides outside-in testing of iOS apps in Simulator, enabling end-to-end verification by interacting with UI and diagnosing failures.
npx playbooks add skill mikekelly/outside-in-testing-ios-apps --skill outside-in-testing-ios-appsReview the files below or copy the command above to add this skill to your agents.
---
name: outside-in-testing-ios-apps
description: "Test iOS apps running in Simulator from the outside in. Use PROACTIVELY when you need to verify an iOS app works, run acceptance tests, check UI behavior, perform outside-in testing, end-to-end testing, functional testing, exploratory testing, or smoke testing of iOS apps. Covers launching test flows, verifying screens, interacting with UI elements, and diagnosing failures. Triggers on: test my app, verify the app, check the UI, does this work, acceptance test, outside-in test, e2e test, functional test, smoke test, exploratory test."
---
<objective>
Guide agents through outside-in testing of iOS apps running in Simulator. The agent interacts with the app as a real user would — navigating screens, tapping buttons, entering text, and verifying that the app behaves correctly. When the app misbehaves, the agent diagnoses failures using simulator logs.
</objective>
<tool>
All interactions use the AXe CLI (`axe`), a tool for driving iOS Simulators via accessibility APIs and HID.
**Installation (prerequisite):**
```bash
brew install cameroncooke/axe/axe
```
The iOS Simulator must be running with the app under test.
</tool>
<quick_start>
1. Find the simulator: `axe list-simulators` — look for a Booted simulator, capture its UDID
2. Screenshot current state: `axe screenshot --udid {UDID} --output /tmp/screen.png`
3. Describe the UI: `axe describe-ui --udid {UDID}`
4. Interact: `axe tap --label "Button" --udid {UDID}`
5. Repeat: screenshot → describe → interact → verify
</quick_start>
<process>
Follow an **Observe → Act → Verify** loop for each step of the test flow.
**Observe**
- Take a screenshot: `axe screenshot --udid {UDID} --output /tmp/screen.png`
- Read the accessibility tree: `axe describe-ui --udid {UDID}`
- To inspect a specific point: `axe describe-ui --point 100,200 --udid {UDID}`
**Act** — choose based on what you need to do:
- Tap by accessibility label: `axe tap --label "Login" --udid {UDID}`
- Tap by accessibility identifier: `axe tap --id "loginButton" --udid {UDID}`
- Tap by coordinates (fallback): `axe tap -x 200 -y 400 --udid {UDID}`
- Type text: `axe type '[email protected]' --udid {UDID}`
- Type from stdin: `echo "text" | axe type --stdin --udid {UDID}`
- Type from file: `axe type --file input.txt --udid {UDID}`
- Scroll: `axe gesture scroll-down --udid {UDID}`
- Swipe: `axe swipe --start-x 200 --start-y 500 --end-x 200 --end-y 200 --duration 0.3 --udid {UDID}`
- Advanced touch (hold): `axe touch -x 150 -y 250 --down --up --delay 1.0 --udid {UDID}`
- Press key: `axe key 40 --udid {UDID}` (Enter)
- Key sequence: `axe key-sequence --keycodes 11,8,15,15,18 --udid {UDID}`
- Home button: `axe button home --udid {UDID}`
- Siri: `axe button siri --udid {UDID}`
**Verify**
After each action, take a screenshot and check whether the expected change occurred. If something is wrong, note the issue and investigate.
**Timing controls** — add delays when animations or loading cause flakiness:
- `--pre-delay 0.5` — wait before acting
- `--post-delay 1.0` — wait after acting
- `--duration 0.3` — control gesture speed
</process>
<gesture_presets>
Available preset gestures (use with `axe gesture {preset} --udid {UDID}`):
- `scroll-up`, `scroll-down`, `scroll-left`, `scroll-right`
- `swipe-from-left-edge`, `swipe-from-right-edge`
- `swipe-from-top-edge`, `swipe-from-bottom-edge`
For specific screen sizes: `axe gesture swipe-from-left-edge --screen-width 430 --screen-height 932 --udid {UDID}`
</gesture_presets>
<recording>
Record video of test sessions for evidence or debugging:
- Record MP4: `axe record-video --udid {UDID} --fps 15 --output recording.mp4`
- Lower quality/size: `axe record-video --udid {UDID} --fps 10 --quality 60 --scale 0.5 --output recording.mp4`
- Stream MJPEG: `axe stream-video --udid {UDID} --fps 10 --format mjpeg > stream.mjpeg`
</recording>
<diagnosing_failures>
When the app is not behaving as expected, fetch simulator logs to diagnose:
```bash
# Stream logs from the booted simulator (Ctrl+C to stop)
xcrun simctl spawn booted log stream --level debug --predicate 'subsystem == "com.apple.UIKit" OR processImagePath CONTAINS "{APP_NAME}"' 2>&1 | head -200
# Get recent log entries
xcrun simctl spawn booted log show --last 5m --predicate 'processImagePath CONTAINS "{APP_NAME}"' --style compact
# Check for crash logs
ls ~/Library/Logs/DiagnosticReports/*{APP_NAME}* 2>/dev/null
```
Replace `{APP_NAME}` with the app's process name (visible in `describe-ui` output or from `xcrun simctl listapps booted`).
**If this is a React Native app**, invoke the `debugging-react-native` skill (https://github.com/mikekelly/debugging-react-native) for React Native-specific diagnosis including Metro bundler issues, JS errors, and red box screens.
</diagnosing_failures>
<gotchas>
- **Labels with special characters fail**: `axe tap --label "Reload (⌘R)"` breaks. Use coordinates from `describe-ui` instead.
- **Tapping input fields may show context menus**: iOS may show "Paste"/"AutoFill" instead of the keyboard. Tap elsewhere to dismiss, then tap the field again or just use `axe type`.
- **Coordinates are in points, not pixels**: Use values from `describe-ui` directly — do not multiply by screen scale.
- **React Native apps need Metro bundler**: If you see "No script URL provided", Metro isn't running. Start it with `npm start` in the app directory.
</gotchas>
<success_criteria>
Testing is complete when:
- App launched and initial screen verified via screenshot
- Core user flows exercised (navigate, interact, verify outcomes)
- Key interactive elements respond correctly
- No crashes or unhandled errors encountered (or failures diagnosed via logs)
- Findings documented: what worked, what failed, and why
</success_criteria>
This skill guides outside-in testing of iOS apps running in the Simulator using the AXe CLI. It focuses on driving the app like a real user — launching flows, interacting with UI, verifying screens, and diagnosing failures with simulator logs. Use it to run acceptance, end-to-end, functional, exploratory, or smoke tests and produce repeatable evidence of app behavior.
The agent uses AXe CLI commands to Observe → Act → Verify in a loop: capture screenshots, read the accessibility tree, interact (taps, typing, gestures), then re-check UI state. All interactions target a Booted Simulator identified by its UDID. When behavior deviates from expectations the skill collects simulator logs and crash reports to diagnose root causes.
What do I need installed before using this skill?
Install AXe CLI (brew install cameroncooke/axe/axe) and run the iOS Simulator with the app under test.
How do I choose between tapping by label, id, or coordinates?
Prefer accessibility labels or identifiers. Use coordinates only when labels contain special characters or are unreliable; obtain coordinates from `axe describe-ui`.
How do I diagnose intermittent failures?
Add pre/post delays, record a video of the session, and stream or capture simulator logs and recent crash reports for analysis.