device-testing

SKILL.md

Use bunx xcobra to interact with iOS simulators and debug Expo apps.

Inspecting the UI

Get the accessibility tree to understand current screen state:

bunx xcobra sim xml

This returns XML with all UI elements, their labels, identifiers, and positions. Use this to:

  • Find element identifiers for tapping
  • Verify UI state after actions
  • Debug layout issues

Tapping Elements

Tap by accessibility label (preferred):

bunx xcobra sim tap --label "Submit"

Tap by accessibility identifier:

bunx xcobra sim tap --id "submit-button"

Tap by coordinates:

bunx xcobra sim tap --x 200 --y 400

Add delays for animations:

bunx xcobra sim tap --label "Next" --pre-delay 500 --post-delay 300

Typing Text

Type text into focused input:

bunx xcobra sim type "Hello World"

Type from stdin:

echo "test@example.com" | bunx xcobra sim type --stdin

Gestures

Preset gestures:

bunx xcobra sim gesture scroll-up
bunx xcobra sim gesture scroll-down
bunx xcobra sim gesture swipe-from-left-edge

Custom swipe:

bunx xcobra sim swipe --start-x 200 --start-y 400 --end-x 200 --end-y 100

Hardware Buttons

Press hardware buttons:

bunx xcobra sim button home
bunx xcobra sim button lock
bunx xcobra sim button siri

Screenshots

Capture screenshot:

bunx xcobra sim screenshot --output screenshot.png

Video Recording

Record simulator video:

bunx xcobra sim record-video --output recording.mp4

Evaluating JavaScript

Execute JS in the running Expo app:

bunx xcobra expo eval "Date.now()"

Get app state:

bunx xcobra expo eval "global.__REDUX_STORE__?.getState()"

Call exposed functions:

bunx xcobra expo eval "globalThis.testHelper?.getCurrentRoute()"

Console Logs

Stream console output:

bunx xcobra expo console

JSON format for parsing:

bunx xcobra expo console --json

Network Monitoring

Monitor network requests:

bunx xcobra expo network

Reloading the App

Trigger a reload to refresh the JavaScript bundle:

bunx xcobra expo reload

This is useful when:

  • The Metro connection becomes stale
  • Hot reload isn't picking up changes
  • The app state needs a fresh start
  • Deep links or navigation seem stuck

Crash Reports

View latest crash:

bunx xcobra crash latest

List recent crashes:

bunx xcobra crash list

Show specific crash:

bunx xcobra crash show <crash-id>

Source Inspection

List loaded scripts:

bunx xcobra expo src scripts

Get source code by script ID:

bunx xcobra expo src source <script-id>

List Metro modules:

bunx xcobra expo src modules

Simulator Management

List all simulators:

bunx xcobra sim list

Target specific simulator:

bunx xcobra sim tap --udid "DEVICE-UDID" --label "OK"

Testing Workflow

  1. Get current UI state

    bunx xcobra sim xml
    
  2. Perform action

    bunx xcobra sim tap --label "Login"
    
  3. Wait and verify

    sleep 1
    bunx xcobra sim xml | grep "Welcome"
    
  4. Check for errors

    bunx xcobra expo console --json | head -20
    

Verifying Screen Content

After navigating, verify you're on the expected screen:

# Check for expected text content
bunx xcobra sim xml | grep -i "expected title"

# Get full accessibility tree and search for elements
bunx xcobra sim xml > /tmp/ui.xml && cat /tmp/ui.xml

Use JavaScript eval to check the current route:

bunx xcobra expo eval "window.location?.pathname"

Troubleshooting Unexpected Routes

If deep links navigate to the wrong screen or you see unexpected content:

1. Check the current route in the app:

bunx xcobra expo eval "globalThis.testHelper?.getCurrentRoute()"

2. Verify the app directory structure:

Look for unexpected index routes that may be intercepting navigation:

# List all index files - these define default routes
find app -name "index.tsx" -o -name "index.ts" -o -name "index.js"

# Check for index routes inside groups that may override expected behavior
find app -path "*/(*)/*" -name "index.*"

3. Common issues:

  • Unexpected index in a group: A file like app/(tabs)/index.tsx will be the default route for the (tabs) group, potentially overriding app/index.tsx
  • Missing layout: Groups need a _layout.tsx to properly nest routes
  • Conflicting routes: Two files resolving to the same URL path

4. Verify route structure matches expectations:

# List all route files
find app -name "*.tsx" | grep -v "_layout" | sort

# Check group structure
find app -type d -name "(*)"`

5. Test deep link resolution:

# Open a deep link and immediately check the route
xcrun simctl openurl booted "myapp://settings" && sleep 1 && bunx xcobra expo eval "window.location?.pathname"

Exposing Test Helpers

Add global helpers in your app for testing:

if (__DEV__) {
  globalThis.testHelper = {
    getCurrentRoute: () => navigationRef.current?.getCurrentRoute(),
    getState: () => store.getState(),
    resetApp: () => { /* reset logic */ },
  };
}

Then call via eval:

bunx xcobra expo eval "testHelper.getCurrentRoute()"
Weekly Installs
3
GitHub Stars
32
First Seen
Jan 25, 2026
Installed on
antigravity3
claude-code3
opencode2
github-copilot2
codex2
kimi-cli2