In our last post, [part 1](/content/blog/pokedex-ui-testing-series-getting-started-with-maestro-in-expo-react-native-part-1/index.html), we talked about Maestro, a go-to UI testing tool for Expo React Native apps. In [Part 2](/content/blog/pokedex-ui-testing-series-a-guide-to-end-to-end-react-native-testing-with-maestro-part-2/index.html), we talked about End-to-End UI testing of the app. This time, we’re stepping up with [**Maestro Cloud**](https://docs.maestro.dev/cloud/run-maestro-tests-in-the-cloud), giving us more flexibility and features. To make this shift smooth, we’re using GitHub Actions, a handy CI/CD tool that’s great for automating tasks.

For those who want a **quick start**, you can jump right in by cloning our repo. Our GitHub repository has you covered. All the setup code and necessary configurations are readily available there. Instead of navigating through the entire blog, you can simply clone the repo and get started.

**GitHub repository:**

[GitHub - thekharche/maestro-expo-pokedexContribute to thekharche/maestro-expo-pokedex development by creating an account on GitHub.github.com](https://github.com/thekharche/maestro-expo-pokedex)

But if you’re here for the details, let’s dive in!

## **Why Maestro Cloud?**

Maestro Cloud streamlines our UI testing in several ways:

**1\. Efficiency**: No need for manual simulator or emulator setups. Just upload the app and Flows, and it’s good to go.

**2\. Robust Features**:

- Managed iOS and Android testing environments.
- Parallel test runs for faster feedback.
- Comprehensive logs, screen recordings, and outputs for detailed insights.

**3\. Seamless CI Integration**: Easily integrates with popular platforms like GitHub Actions, Bitrise, CircleCI, Jenkins, Azure Pipelines, TravisCI, GitLab CI/CD, Bitbucket Pipelines, etc.

**4\. Versatility**: Supports iOS, Android, React Native, and Flutter, ensuring broad testing coverage.

**5\. Security**: With SOC 2 compliance, our data’s security and privacy are top-notch.

## Automating with GitHub Actions

**File:**`.github/workflows/eas-build.yaml`

GitHub Actions provides automation for CI/CD. Here’s our setup:

```
name: EAS Builds

on:
  push:
    branches: [main]

jobs:
  eas-build:
    name: Maestro Cloud tests for Expo EAS
    timeout-minutes: 15
    runs-on: ubuntu-latest

steps:
      - name: Setup Repository
        uses: actions/checkout@v3

- name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18.x
          cache: "npm"

- name: Setup EAS
        uses: expo/expo-github-action@v8
        with:
          eas-version: latest
          token: ${{ secrets.EXPO_TOKEN }}

- name: Install Dependencies
        run: npm install

- name: Build Request for Android
        run: eas build --platform android --non-interactive --no-wait --profile internal-apk
        shell: bash

- name: Build Request for iOS
        run: eas build --platform ios --non-interactive --no-wait --profile internal-simulator
        shell: bash
```

This workflow, named “EAS Builds”, triggers on every `push` to the `main` branch and runs our Maestro Cloud tests.

### **Setup Expo Token**

If you don’t already have an `EXPO_TOKEN`, create one from your account settings at [https://expo.dev/](https://expo.dev/):

Remember to add the `EXPO_TOKEN` secret for GitHub Actions in GitHub repository settings:

**Build Request for Android:**

This step initiates an Android build using EAS. It runs in a non-interactive mode (no user input required), doesn’t wait for the build to finish (`--no-wait`), and uses the `internal-apk` profile for specific build configurations.

**Build Request for iOS:**

Similarly, this step starts an iOS build using EAS. It’s also non-interactive, doesn’t wait for completion, and uses the `internal-simulator` profile tailored for iOS builds.

## Post-Build Maestro Cloud Integration

**File:**`scripts/eas-build-on-success.sh`

After the app builds, we run our UI tests on Maestro Cloud:

```
#!/usr/bin/env bash

# Check if the environment is local
if [ "$ENV_TYPE" = "local" ]; then
    echo "this is local environment"
    exit 0
fi

# If not local, continue with the provided script
set -eox pipefail

curl -Ls "https://get.maestro.mobile.dev" | bash
export PATH="$PATH":"$HOME/.maestro/bin"

if [ "$EAS_BUILD_PLATFORM" = "ios" ]; then
   APP_EXECUTABLE_PATH=/Users/expo/workingdir/build/ios/build/Build/Products/Release-iphonesimulator/maestroexpopokedex.app
   brew install java
   echo 'export PATH="/opt/homebrew/opt/openjdk/bin:$PATH"' >> ~/.zshrc
   sudo ln -sfn /opt/homebrew/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
   export CPPFLAGS="-I/opt/homebrew/opt/openjdk/include"
   maestro cloud -e APP_ID=com.thekharche.maestroexpopokedex --api-key=$MAESTRO_API_KEY --app-file=$APP_EXECUTABLE_PATH --flows=.maestro/

else
   APP_EXECUTABLE_PATH=/home/expo/workingdir/build/android/app/build/outputs/apk/release/app-release.apk
   maestro cloud -e APP_ID=com.thekharche.maestroexpopokedex --api-key=$MAESTRO_API_KEY --app-file=$APP_EXECUTABLE_PATH --flows=.maestro/
fi
```

This script checks the environment, sets up Maestro, and runs platform-specific UI tests.

You can find your **Maestro API Key** here: [https://app.maestro.dev/](https://app.maestro.dev/)

Remember to set up your `MAESTRO_API_KEY` in Expo Secrets.

The `eas-build-on-success.sh` script is crucial for integrating Maestro Cloud into the post-build process. It ensures that once the application is built, it undergoes UI testing using Maestro Cloud. Here’s a breakdown of its key components:

**Environment Check:**

```
if [ "$ENV_TYPE" = "local" ]; then
    echo "this is local environment"
    exit 0
fi
```

The script first checks if the environment is `local`. If true, it prints “this is local environment” and exits.

**Maestro Setup:**

```
curl -Ls "https://get.maestro.mobile.dev" | bash
export PATH="$PATH":"$HOME/.maestro/bin"
```

This section fetches and installs Maestro. The Maestro binaries are then added to the system’s PATH, ensuring subsequent commands can access Maestro tools without specifying their full paths.

**Platform-Specific UI Testing:**

Depending on whether the build platform is **iOS** or **Android**, different sets of commands are executed:

```
if [ "$EAS_BUILD_PLATFORM" = "ios" ]; then
  // hidden for brevity
else
  // hidden for brevity
fi
```

In essence, the `eas-build-on-success.sh` script serves as a bridge between the build process and Maestro Cloud-based UI testing. It ensures that after a successful build, the app is tested in the cloud environment, leveraging Maestro’s capabilities.

Learn more about [**EAS Build lifecycle npm hooks**](https://docs.expo.dev/build-reference/npm-hooks/)

## EAS Configuration for Expo

**File:**`eas.json`

This file defines various build profiles for both Android and iOS.

```
{
  "cli": {
    "version": ">= 3.10.2"
  },
  "build": {
    // hidden for brevity
    "development-simulator": {
      "developmentClient": true,
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    },
    "internal-simulator": {
      "distribution": "internal",
      "ios": {
        "simulator": true
      }
    },
    "development-apk": {
      "developmentClient": true,
      "distribution": "internal",
      "android": {
        "buildType": "apk"
      }
    },
    "internal-apk": {
      "distribution": "internal",
      "android": {
        "buildType": "apk"
      }
    },
    // hidden for brevity
  },
  "submit": {
    "production": {}
  }
}
```

### **Local Builds**:

- **iOS**: Uses the `development-simulator` profile, tailored for local testing on iOS simulators.
- **Android**: Uses the `development-apk` profile, designed for local testing with Android APKs.

[Refer to part 1 of the series for local builds use case](/content/blog/pokedex-ui-testing-series-getting-started-with-maestro-in-expo-react-native-part-1/index.html)

### **Cloud Builds**:

- **iOS**: Switches to the `internal-simulator` profile, optimized for cloud-based testing on iOS simulators.
- **Android**: Adopts the `internal-apk` profile, configured for cloud-based testing with Android APKs.

In essence, while local builds employ “development” profiles, cloud builds transition to “internal” profiles to cater to their specific environments.

## Package Configuration and Scripts

**File:**`package.json`

The backbone of our project:

```
{
  "name": "mobile",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "eas-build-on-success": "bash ./scripts/eas-build-on-success.sh",
    // hidden for brevity
  },
  // hidden for brevity
}
```

The `eas-build-on-success` script triggers our post-build bash script, ensuring Maestro Cloud integration post-build.

## Maestro — Run when Local

**File:**`.maestro/journey/pokemon-lookup.yaml`

Here, we’ve introduced an optional flow. If you choose to set the `TARGET_ENV` variable to ‘local’, Maestro will execute the `local-setup.yaml` flow.

```
appId: ${APP_ID}
---
- launchApp:
    clearState: true

- runScript: ../common/init.js

- runFlow:
    when:
      true: ${TARGET_ENV == 'local'}
    file: ../common/local-setup.yaml

# hidden for brevity
```

This allows you to tailor your testing approach based on the environment if desired.

The `local-setup.yaml` flow is activated only when `TARGET_ENV` is set to ‘local’. This means that when tests are executed on Maestro Cloud, this particular flow is bypassed.

For local testing, run:

```
maestro test -e TARGET_ENV=local -e APP_ID=com.thekharche.maestroexpopokedex .maestro/journey/pokemon-lookup.yaml
```

Using the above command, the `local-setup.yaml` flow is included, allowing for a tailored testing experience when running tests locally.

## The Maestro — Expo EAS Cloud UI test Workflow

**1.** Every time a developer pushes code to the repository, the automation process begins. **GitHub Actions** immediately sets up a dedicated environment, creating a uniform base for all subsequent tasks.

**2\. Expo EAS** takes charge, constructing the application for both Android and iOS platforms.

**3.** With the builds in place, **Maestro Cloud** conducts UI tests on the app.

## Conclusion

Taking UI testing to the cloud with Maestro and automating with GitHub Actions ensures a robust, scalable, and efficient testing process. This setup gives us confidence in our Expo React Native apps’ consistent and high-quality user experience.

## References

Thanks to [https://github.com/lingvano/react-native-eas-maestro](https://github.com/lingvano/react-native-eas-maestro) for inspiration.

**Expo Dev:**

[Expo — Build native apps with ReactExpo is the fastest way to build native apps for iOS, Android, and the web with React.expo.dev](https://expo.dev/)

**Maestro Cloud:**

[Run tests on Maestro Cloud \| Maestro Cloud \| Maestro DocsUpload and execute your Maestro tests on Cloud infrastructure using the CLI.docs.maestro.dev](https://docs.maestro.dev/cloud/run-maestro-tests-in-the-cloud)

**Maestro Cloud Console:**

[Maestro, End-to-End UI Testing for Mobile and WebCreate reliable UI tests fast. Run free on CLI or Maestro Studio, then scale on Maestro Cloud. Automate iOS, Android and web with simple setup.maestro.dev](https://app.maestro.dev/)

**Part 1 of Pokedex UI Testing Series with Local Setup:**

[Pokedex UI Testing Series: Getting Started with Maestro in Expo &#38; React Native — Part 1Setup Expo Development builds for Android &#38; iOSmaestro.dev](/content/blog/pokedex-ui-testing-series-getting-started-with-maestro-in-expo-react-native-part-1/index.html)

**GitHub Source Code:**

**EAS Build Lifecyle npm hooks:**

[Build lifecycle hooksLearn how to use the EAS Build lifecycle hooks with npm to customize your build process.docs.expo.dev](https://docs.expo.dev/build-reference/npm-hooks/)
