Native testing

May 11, 2026 · View on GitHub

testIDs in src/testIds.ts are surfaced as native identifiers on both platforms:

  • iOStestID becomes accessibilityIdentifier. XCUITest sees them via app.<query>["the.id"].
  • AndroidtestID becomes the View's resource-id (technically the nativeID). Espresso matches them with withResourceName("the.id").

This means every flow we already wrote in Maestro or Appium can be re-implemented in Espresso or XCUITest without changing a single identifier in the app source.

Project layout

tests/
├── maestro/        # YAML flows (cross-platform)
├── appium/         # Phase 6
├── espresso/       # Kotlin templates — copy into android/app/src/androidTest/
└── xcuitest/       # Swift templates — add as a UI Test target in Xcode

We keep the test sources in tests/<framework>/ rather than burying them inside the platform projects, so:

  • Contributors discover them at the top level.
  • One PR can update tests for multiple frameworks.
  • The native projects under ios/ and android/ can be regenerated by expo prebuild --clean without losing test sources.

Running

See tests/espresso/README.md and tests/xcuitest/README.md for the per-framework setup steps. In short:

# Android — Espresso
cp tests/espresso/*.kt android/app/src/androidTest/java/app/frontrow/qa/
cd android && ./gradlew :app:connectedAndroidTest

# iOS — XCUITest (after creating a UI Test target named "FrontRowUITests")
cd ios
xcodebuild test \
  -workspace FrontRow.xcworkspace \
  -scheme FrontRow \
  -destination 'platform=iOS Simulator,name=iPhone 15' \
  -only-testing:FrontRowUITests

The native showcase screen

A hand-rolled Swift UIViewController and Kotlin AppCompatActivity ship in native-showcase/ and get copied into the host iOS/Android projects on every expo prebuild by plugins/with-native-showcase.js. Open it from the app at Debug tab → Native demo, or programmatically:

import { openNativeDemo } from './src/utils/nativeDemo';
openNativeDemo();

JS calls go through NativeModules.FrontRowNativeDemo.open(). On iOS this is wired up in FrontRowNativeDemoBridge.m via RCT_EXTERN_REMAP_MODULE; on Android via NativeDemoPackage.kt. The native screens set accessibilityIdentifier (iOS) and contentDescription (Android) to the same nativeDemo.* strings registered in src/testIds.ts, so XCUITest, Espresso, and Maestro all match against the same names.

The cross-platform Maestro flow that exercises the round-trip lives at tests/maestro/native/native-demo.yaml.

The native test scaffolds at native-showcase/tests/espresso/ and native-showcase/tests/xcuitest/ are reference templates — they aren't wired into a runnable test target by default (iOS UI Test target setup happens in Xcode; Android androidTest source set needs adding to app/build.gradle). See the per-framework READMEs for setup.