An Interest In:
Web News this Week
- April 1, 2024
- March 31, 2024
- March 30, 2024
- March 29, 2024
- March 28, 2024
- March 27, 2024
- March 26, 2024
React Native e2e tests and Automatic Deploys (Detox Fastlane CircleCI)
A little bit of context (skippable)
These past weeks I've been struggling trying to set up a pipeline where for every PR pointing to our staging branch, e2e tests run automatically, and for every PR that gets merged, test flight builds, and google internal beta builds are created. My experience has been... difficult, but it doesn't mean yours should too.
tl;dr; PAIN
.
What do I need for this?
1. Circle CI's performance plan.
Since we are going to use macs for building our apps. If you only need android builds, you can easily achieve this with github actions, see
this project for an example and this amazing post.
2. Apple connect account & Google play console account.
This will be needed for automatic deployments(2nd part).
3. Patience
Trust me, you'll need it. CIs can smell fear.
Let's get this started
1. Add detox into your react native project.
Please, follow
this guide using JEST step by step in order to have it correctly configured in your project. Here is an
example of a .detoxrc.json.
Once you can run e2e tests locally, you're ready to go for the next step.
2. Set up CircleCI.
If you don't have CircleCI, you can learn how to add it here.
Don't worry too much about the content on the config.yml, since we are going to completely modify it. :)
At this point, you should have a folder named .circleci/
with a config.yml
file.
Let's make magic using orbs!
Add this to your /app/build.gradle
since we are going to use the react-native-circleci-orb.
task downloadDependencies() { description 'Download all dependencies to the Gradle cache' doLast { configurations.findAll().each { config -> if (config.name.contains("minReactNative") && config.canBeResolved) { print config.name print '
' config.files } } }}
Feeling lucky?
React native community example of how to use this orb is the following:
version: 2.1orbs: rn: react-native-community/[email protected]# Custom jobs which are not part of the Orbjobs: checkout_code: executor: rn/linux_js steps: - checkout - persist_to_workspace: root: . paths: . analyse_js: executor: rn/linux_js steps: - attach_workspace: at: . - rn/yarn_install - run: name: Run ESLint command: yarn eslint - run: name: Flow command: yarn flow - run: name: Jest command: yarn jestworkflows: test: jobs: # Checkout the code and persist to the Workspace # Note: This is a job that is defined above and not part of the Orb - checkout_code # Analyze the Javascript using ESLint, Flow, and Jest # Note: This is a job that is defined above and not part of the Orb - analyse_js: requires: - checkout_code # Build the Android app in debug mode - rn/android_build: name: build_android_debug project_path: "android" build_type: debug requires: - analyse_js # Build and test the Android app in release mode # Note: We split these into separate jobs because we can build the Android app on a Linux machine and preserve the expensive MacOS executor minutes for when it's required - rn/android_build: name: build_android_release project_path: "android" build_type: release requires: - analyse_js - rn/android_test: detox_configuration: "android.emu.release" requires: - build_android_release # Build the iOS app in release mode and do not run tests - rn/ios_build: name: build_ios_release project_path: ios/Example.xcodeproj device: "iPhone X" build_configuration: Release scheme: Example requires: - analyse_js # Build and test the iOS app in release mode - rn/ios_build_and_test: project_path: "ios/Example.xcodeproj" device: "iPhone X" build_configuration: "Release" scheme: "Example" detox_configuration: "ios.sim.release" requires: - analyse_js
But there is a catch, in my experience, it did not work. Here are the docs of every helper function on this orb.
What's next?
Welp let's go step by step and create something that works ;)
Orb
version: 2.1orbs: rn: react-native-community/[email protected]
Note that we call it rn
, this name can be whatever you want, and it's just used to specify when a job is coming from the orb. Ex. rn/yarn_install
Jobs
checkout_code
Check out the code and persist to the Workspace, needed in order to do stuff in the project root.
checkout_code: executor: name: rn/linux_js node_version: "12" steps: - checkout - persist_to_workspace: paths: . root: .
analyse_js
Running jest test on Linux. Note how we use an executor from our orb and define the node_version version for our project.
analyse_js: executor: name: rn/linux_js node_version: "12" steps: - attach_workspace: at: . - rn/yarn_install - run: command: yarn test name: Run Tests
Android e2e
In a perfect world, the example on the docs is all you need. But this is programming, specifically, React native that we're talking about, the example is the following:
- rn/android_build: build_type: debug name: build_android_debug project_path: android requires: - analyse_js- rn/android_build: build_type: release name: build_android_release project_path: android requires: - analyse_js
The main issue with this approach is that rn/android_build builds the app as a normal build and not as a detox build which can lead to weird issues and false-negative e2e tests.
So... yeah, we have to re-do this step manually, but feel free to try! If it works for you, shame me on Twitter!.
Please read the comments to understand what is going on here.
android_e2e_test: # Using a mac (: executor: name: rn/macos steps: - attach_workspace: at: . - rn/setup_macos_executor: homebrew_cache: true node_version: "12" - rn/yarn_install: # basically because of this https://github.com/react-native-community/react-native-circleci-orb/issues/66 cache: false - run: # For my app and react native in general java8 is needed. The default version on this executor was default to java10 for some reason, so this kinda solve that issue. # just installing java, android sdk, and needed tools. command: > java -version brew tap adoptopenjdk/openjdk brew install --cask adoptopenjdk/openjdk/adoptopenjdk8 java -version export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) mkdir -p ~/.android && touch ~/.android/repositories.cfg java -version yes | sdkmanager "platform-tools" "tools" >/dev/null yes | sdkmanager "platforms;android-29" "system-images;android-29;default;x86_64" >/dev/null yes | sdkmanager "emulator" --channel=3 >/dev/null yes | sdkmanager "build-tools;29.0.2" >/dev/null yes | sdkmanager --licenses >/dev/null yes | sdkmanager --list name: Install Android Emulator shell: /bin/bash -e - run: command: | adb start-server adb devices adb kill-server ls -la ~/.android name: ADB Start Stop - run: # Note we are using a pixel_xl as the test device, feel free to change it for one better fits your app command: | export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) avdmanager create avd --force --name Pixel_2_API_29 --package "system-images;android-29;default;x86_64" --tag default --device pixel_xl name: Create Android Emulator - run: background: true command: | export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) $ANDROID_HOME/emulator/emulator @Pixel_2_API_29 -version $ANDROID_HOME/emulator/emulator @Pixel_2_API_29 -cores 2 -gpu auto -accel on -memory 2048 -no-audio -no-snapshot -no-boot-anim -no-window -logcat *:W | grep -i 'ReactNative\|com.reactnativecommunity' name: Start Android Emulator (background) - run: command: > # export JAVA_HOME=$(/usr/libexec/java_home -v 1.8) export BOOT="" echo "Waiting for AVD to finish booting" export PATH=$(dirname $(dirname $(command -v android)))/platform-tools:$PATH until [[ "$BOOT" =~ "1" ]]; do sleep 5 export BOOT=$(adb -e shell getprop sys.boot_completed 2>&1) done sleep 15 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 echo "Android Virtual Device is now ready." name: Wait for AVD to be ready no_output_timeout: 5m # Creates the detox build using the orb job - rn/detox_build: configuration: "android.emu.release" # Tests the app, you can use rn/detox_test, but I wanted to take screenshots when test fails so I can have a better idea of why did they fail. - run: command: >- detox test -c android.emu.release -l warn --headless --take-screenshots failing --artifacts-location /tmp/detox_artifacts name: Detox Test # Save the screenshots as artifacts, you can see then in the artifact tab for the job in CircleCI - store_artifacts: path: /tmp/detox_artifacts
Note that all of this can be achieved using the rn/linux_android executor.
iOS e2e
In a perfect world, the example on the docs is all you need. And it was for me... until it wasn't. Try the following, if that works for you, shame me on Twitter!.
# Build and test the iOS app in release mode- rn/ios_build_and_test: project_path: "ios/Example.xcodeproj" device: "iPhone X" build_configuration: "Release" scheme: "Example" detox_configuration: "ios.sim.release" requires: - analyse_js
Fortunately, ios is better than android. Yeah, I said it... At least development wise. In order to recreate the ios_build_and_test all we need is:
# Build and test the iOS app in release modeios_e2e_test: executor: rn/macos steps: - checkout - attach_workspace: at: . - rn/setup_macos_executor: homebrew_cache: true node_version: "12" - rn/ios_simulator_start: device: "iPhone 11" - rn/yarn_install: # basically because of this https://github.com/react-native-community/react-native-circleci-orb/issues/66 cache: false - rn/pod_install: pod_install_directory: ios # Yep, it doesn't really matter if you don't run detox build for ios, it works like a charm. But if you prefer, you can replace this step with a custom one. - rn/ios_build: build_configuration: "Release" cache: false derived_data_path: "ios/build" device: "iPhone 11" project_path: "ios/example.xcworkspace" project_type: workspace scheme: "example" - run: command: >- detox test -c ios.sim.release -l warn --headless --take-screenshots failing --artifacts-location /tmp/detox_artifacts name: Detox Test - store_artifacts: path: /tmp/detox_artifacts
Congratulations! You have e2e tests running in your app! Give yourself a pat in the back and go get a drink, because Fastlane is coming.
The hardest thing is doing the configurations for your project. Feel free to ask in the comments, but fastlane documentation should be enough to get you ready for the next steps.
Checkout these if you need a place to start:
Fastlane android
This is easier than what you already did. :) All we need is to install Fastlane on Linux and run our Fastlane lane.
fastlane_android_internal: executor: rn/linux_android steps: - attach_workspace: at: . - rn/yarn_install - run: command: gem install bundler name: Install bundler - run: command: gem install fastlane name: Install Fastlane # Note that my lane is name upload_to_googleplay replaced for yours - run: # can be fancier and use working_directory command: cd android && fastlane upload_to_googleplay name: Upload to google play via Fastlane
Fastlane ios
I'm pretty sure adding Fastlane to ios was not an easy task. So... Congratulations Shinji! These are basically the same steps but for ios.
# submit app to apple connect testflightfastlane_ios_testflight: executor: name: rn/macos steps: - attach_workspace: at: . - rn/yarn_install: cache: false - run: working_directory: ios command: pod install - run: command: gem install bundler name: Install bundler - run: command: gem install fastlane name: Install Fastlane - run: working_directory: ios command: fastlane beta name: Upload to Testflight via Fastlane
So... tips for Fastlane.
- Fastlane Docs.
- 2fa for apple connect.
- CircleCI Docs.
- Use date for build numbers. (There are other ways to get incremental build numbers, if you want to try them, Can't recommend any since I haven't used any for the ci).
- android:in the build.gradle
date.getTime() % 100_000_000_000;
- ios:in fastlane/Fastfile
build_number: DateTime.now.strftime("%Y%m%d%H%M")
- android:in the build.gradle
One more thing
In order to make everything work, we need to create a workflow where we define the order of the steps.
So... here's a proposal:
workflows: # name of the workflow main: jobs: - checkout_code # Do jest tests - analyse_js: requires: - checkout_code # Build and test the android app in release mode - android_e2e_test: requires: - analyse_js # Build and test the iOS app in release mode - ios_e2e_test: requires: - analyse_js # Release apps to stores for testing - fastlane_android_internal: # We only want to deploy to google play when things get merged into the main branch filters: branches: only: - main # Note that e2e need to pass in order to release requires: - android_e2e_test - fastlane_ios_testflight: # We only want to deploy to google play when things get merged into the main branch filters: branches: only: - main # Note that e2e need to pass in order to release requires: - ios_e2e_test
If react native, detox, CircleCI and Fastlane decided you can rest today, you should see something like this in your pipeline.
Original Link: https://dev.to/kyonru/react-native-e2e-tests-and-automatic-deploys-detox-fastlane-circleci-3gkn
Dev To
An online community for sharing and discovering great ideas, having debates, and making friendsMore About this Source Visit Dev To