Labsco
expo logo

android-e2e-testing

✓ Official50,400

by expo · part of expo/expo

Test Expo Router features on Android emulators using ADB. Use after implementing native Android features or when verifying UI behavior on Android.

🔥🔥🔥✓ VerifiedFreeQuick setup
🧰 Not standalone. This skill ships with expo/expo and only works together with that tool — install the tool first, then add this skill.

Test Expo Router features on Android emulators using ADB. Use after implementing native Android features or when verifying UI behavior on Android.

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.


name: android-e2e-testing description: Test Expo Router features on Android emulators using ADB. Use after implementing native Android features or when verifying UI behavior on Android. user-invokable: true

Android E2E Testing for Expo Router

Use adb to manually test Expo Router screens and components on Android emulators.

When to Use

  • After implementing or modifying native Android UI components (toolbars, tabs, menus)
  • When verifying Jetpack Compose components (@expo/ui/jetpack-compose)
  • When running the native-navigation or other E2E apps on Android
  • Before opening a PR that touches Android-specific behavior

Step 1: Build and Launch

Router E2E apps

Package name: dev.expo.routere2e

Each app in apps/router-e2e/__e2e__/ has a corresponding yarn android:<name> script. Check apps/router-e2e/package.json for available scripts.

Copy & paste — that's it
cd apps/router-e2e
yarn android:[APP_NAME]   # e.g. yarn android:native-navigation

If the app is already built, relaunch it:

Copy & paste — that's it
adb shell monkey -p dev.expo.routere2e -c android.intent.category.LAUNCHER 1

To find the package name of any installed app:

Copy & paste — that's it
adb shell pm list packages | grep -i <keyword>

Step 2: Navigate Using UI Dump

CRITICAL: Always use uiautomator dump for element coordinates. Screenshot pixel coordinates have display scaling factors that make them unreliable for adb shell input tap. The UI dump provides actual device coordinates.

Dump the view hierarchy

Copy & paste — that's it
adb shell uiautomator dump /sdcard/ui.xml && adb shell cat /sdcard/ui.xml

This returns XML with every UI element including:

  • text — displayed text
  • content-desc — accessibility description (useful for icon buttons)
  • bounds — position as [left,top][right,bottom]
  • clickable — whether the element responds to taps
  • class — Android view class

Find and tap an element

  1. Search the XML for your target element by text or content-desc
  2. Extract the bounds attribute: bounds="[left,top][right,bottom]"
  3. Calculate center: x = (left + right) / 2, y = (top + bottom) / 2
  4. Tap:
Copy & paste — that's it
adb shell input tap <x> <y>

Example: For bounds="[367,498][714,633]":

  • x = (367 + 714) / 2 = 540
  • y = (498 + 633) / 2 = 565
Copy & paste — that's it
adb shell input tap 540 565

Wait for navigation to settle

After tapping a navigation element, wait before verifying:

Copy & paste — that's it
sleep 1

For slow transitions or heavy screens, use sleep 2.

Step 3: Interact

Tap items

Copy & paste — that's it
adb shell input tap <x> <y>

Scroll

Copy & paste — that's it
# Scroll down
adb shell input swipe 540 1500 540 500 300

# Scroll up
adb shell input swipe 540 500 540 1500 300

# Scroll further (larger distance)
adb shell input swipe 540 1500 540 200 500

Type text

Copy & paste — that's it
adb shell input text "hello%sworld"   # %s = space

Press hardware buttons

Copy & paste — that's it
adb shell input keyevent 4    # Back
adb shell input keyevent 3    # Home
adb shell input keyevent 82   # Menu / React Native dev menu

Long press

Copy & paste — that's it
adb shell input swipe <x> <y> <x> <y> 1000

Step 4: Verify

Visual verification via screenshot

Copy & paste — that's it
adb shell screencap -p /sdcard/screenshot.png && adb pull /sdcard/screenshot.png /tmp/screenshot.png

Then use the Read tool to view /tmp/screenshot.png. Screenshots are useful for:

  • Confirming visual appearance (colors, layout, styling)
  • Verifying toolbar/tab positioning
  • Checking selection states and visual feedback

Note: Use screenshots for visual verification only. For element positions and tapping, always use uiautomator dump.

Programmatic verification via UI dump

Copy & paste — that's it
adb shell uiautomator dump /sdcard/ui.xml && adb shell cat /sdcard/ui.xml

Search the XML for expected content:

  • Verify text content appears
  • Check content-desc for accessibility labels
  • Confirm element presence after navigation
  • Verify selection states (look for checkmarks, content-desc changes)

Check for errors

Copy & paste — that's it
# React Native JS errors
adb logcat -d -s ReactNativeJS:E | tail -20

# Crash logs
adb logcat -b crash -d

# All recent errors
adb logcat -d *:E | tail -30

Step 5: Report Results

After testing, summarize results in a table:

TestResult
Navigation to screenPASS/FAIL
Component renders correctlyPASS/FAIL
Interaction worksPASS/FAIL
No JS errors in logcatPASS/FAIL

Include details for any failures: what was expected vs what happened, relevant logcat output, and screenshots.

Preferably attach screenshots for features you tested.

Testing Jetpack Compose Components

Components from @expo/ui/jetpack-compose (like HorizontalFloatingToolbar, IconButton, Host) render as Compose views inside React Native. In UI dumps they appear as:

  • androidx.compose.ui.platform.ComposeView — the Compose container
  • android.widget.HorizontalScrollView — inside toolbar layouts
  • android.widget.Button — Compose buttons
  • android.view.View with content-desc — icon buttons with accessibility labels

When testing Compose components:

  1. Look for content-desc attributes to identify buttons (e.g., content-desc="Clear selection")
  2. The ComposeView wrapper may have different bounds than the inner interactive elements
  3. Tap the interactive element's bounds, not the container's

Disable Animations (Recommended for Testing)

Disabling animations prevents flaky UI dumps and makes testing more reliable:

Copy & paste — that's it
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0

Re-enable when done:

Copy & paste — that's it
adb shell settings put global window_animation_scale 1
adb shell settings put global transition_animation_scale 1
adb shell settings put global animator_duration_scale 1

Complete Example: Testing a Toolbar Screen

Copy & paste — that's it
# 1. Launch app
adb shell monkey -p dev.expo.routere2e -c android.intent.category.LAUNCHER 1
sleep 2

# 2. Dump UI to find navigation button
adb shell uiautomator dump /sdcard/ui.xml && adb shell cat /sdcard/ui.xml
# Find: content-desc="Android Toolbar" bounds="[367,498][714,633]"
# Center: (540, 565)

# 3. Navigate to screen
adb shell input tap 540 565
sleep 1

# 4. Take screenshot to verify visual appearance
adb shell screencap -p /sdcard/screenshot.png && adb pull /sdcard/screenshot.png /tmp/screenshot.png

# 5. Dump UI to find toolbar buttons
adb shell uiautomator dump /sdcard/ui.xml && adb shell cat /sdcard/ui.xml
# Find buttons by content-desc: "Clear selection", "Select all", "Delete", "Add"

# 6. Test toolbar interactions
adb shell input tap 457 2233  # "Select all" button center
sleep 1

# 7. Verify state changed
adb shell screencap -p /sdcard/screenshot.png && adb pull /sdcard/screenshot.png /tmp/screenshot.png

# 8. Check for errors
adb logcat -d -s ReactNativeJS:E | tail -20