Native testing
May 11, 2026 · View on GitHub
testIDs in src/testIds.ts are surfaced as native identifiers on both platforms:
- iOS —
testIDbecomesaccessibilityIdentifier. XCUITest sees them viaapp.<query>["the.id"]. - Android —
testIDbecomes the View'sresource-id(technically thenativeID). Espresso matches them withwithResourceName("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/andandroid/can be regenerated byexpo prebuild --cleanwithout 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.