Make Fastlane Screengrab Work with React Native

FanchenBao
4 min readFeb 11, 2021
Using fastlane screengrab on react native app is also challenging

This is a sister article of Make Fastlane Snapshot Work with React Native. While fastlane snapshot works with iOS app, fastlane screengrab is its Android version that programmatically takes Android app screenshots using the UI Testing functionality supported by Android Studio. Although the screenshot requirement on Android Play Store is less stringent than that on Apple App Store (no hard requirement on dimension, can skip tablets, etc.), the journey of getting fastlane screengrab to work with React Native is still quite a mouthful. Given that most articles on this topic that we can find are dated, we would like to share our fresh experience working with fastlane screengrab.

Be Careful with The Documentation

The documentation on screengrab is more up to date than the one on Android Screenshot, because the former specifies that the latest version of screengrab is 2.0.0. The latter, however, provides a gem link to screengrab, which says its current version is 1.0.0. Configuring screengrab as 1.0.0 (more on this later) in the gradle dependency does not work.

Preparation for UI Testing

The documentation is not particularly detailed regarding how to modify the Android native code to prepare for UI Testing with expresso, AndroidJUnitRunner, and UI Automator. We have found that the following configurations are necessary.

app_root/android/build.gradle

minSdkVersion must be larger or equal to 18. This is to satisfy UI Automator’s requirement.

app_root/android/app/build.gradle (updated 09/22/2021)

Modify dependencies as follows

dependencies {
...
// fastlane screengrab, falcon is a dependency of screengrab
androidTestImplementation 'com.jraska:falcon:2.2.0'
androidTestImplementation "tools.fastlane:screengrab:2.1.0"
// Espresso dependencies
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// Hamcrest library
androidTestImplementation 'org.hamcrest:hamcrest-library:1.3'

// Core library
androidTestImplementation 'androidx.test:core:1.4.0'

// AndroidJUnitRunner and JUnit Rules
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
}

This configuration mainly comes from Android documentation on building instrumented unit test. However, notice that we do not include the dependency for UI Automator. This is because UI Automator is already provided by screengrab. Including it again will lead to class duplication error. Also notice that we put version 2.1.0 for screengrab.

Next, modify defaultConfig as follows

android {
...
defaultConfig {
...
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}

app_root/android/app/src/debug/AndroidManifest.xml (updated 09/22/2021)

Paste the following config in the manifest file.

<!--For fastlane screengrab use-->
<!-- Allows unlocking your device and activating its screen so UI tests can succeed -->
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- Allows for storing and retrieving screenshots -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Allows changing locales -->
<uses-permission
android:name="android.permission.CHANGE_CONFIGURATION"
tools:ignore="ProtectedPermissions" />
<!-- Indicates the use of the clean status bar feature -->
<uses-feature android:name="tools.fastlane.screengrab.cleanstatusbar"/>
<!-- Allows for changing the status bar -->
<uses-permission
android:name="android.permission.DUMP"
tools:ignore="ProtectedPermissions" />

Create UI Testing file

The UI Testing file should be created in this path

app_root/android/app/src/androidTest/java/ScreenshotTest.java

We have thus named our test file ScreenshotTest.java. Below is a template to get one started on obtaining screenshots.

Notice that we use onView(allOf(withContentDescription(id))) to capture a component which has id as its accessibilityLabel. We stumbled upon this solution in Gergő Nagygyörgy’s article. This is truly a godsend, as the method specified in the documentation simply does not work for us.

Add accessibilityLabel to Dummy Buttons

The method of programmatically capturing screenshot is the same as described in the previous article. In iOS UI Testing, TouchableHighlight is recognizable via testID, but in Android UI Testing, only accessibilityLabel is recognized. This means, for example, in order to make the template shown above work, we must specify accessibilityLabel="some_api_query_button" in the dummy button.

Setup Fastfile to run screengrab

Follow the instructions in the documentation to create a screenshot lane. Notice that we must build two APK for screengrab, one is debug and the other AndroidTest. If any React Native code is modified, both builds must be rebuilt to reflect the change. If ScreenshotTest.java is the only file modified, then only AndroidTest build needs to be rebuilt.

Below is our screenshot lane. We also use Screengrabfile to configure screengrab. For details, refer to the documentation.

desc 'Produce screenshots via screengrab'
lane :screenshots do
gradle(task: 'clean', project_dir: 'android/')
gradle(
task: 'assemble',
build_type: 'Debug',
project_dir: 'android/',
)
gradle(
task: 'assemble',
build_type: 'AndroidTest',
project_dir: 'android/',
)
screengrab
end

Keep Metro Bundler and Device Emulator Running

screengrab requires that both the metro bundler (i.e. npx react-native start) and a desired Android device emulator to be running when screenshots are taken. Surprisingly, these two are not automatically spun up by screengrab. In other words, we need to manually start metro bundler and device emulator before runninng screengrab, or write scripts to initiate them in Fastfile. One benefit of having the device emulator up is that we can observe in real time how the actions specified in ScreenshotTest.java are executed.

Summary

We have presented our experience working with fastlane screengrab on a React Native app. Overall, it is less annoying as fastlane snapshot, but there are still quite a few hurdles to jump through. The parts we think the existing documentation is unclear are:

  1. The native code set up for UI Testing
  2. The method to capture react native component during UI Testing.

We hope this write up will help those who feel a bit overwhelmed when reading the fastlane screengrab documentation for the first time (in our case, we were so overwhelmed that we had to stare at a blank wall to re-contemplate our lives).

--

--